Leetcode刷题记录28——缺失的第一个正数
题源:https://leetcode.cn/problems/first-missing-positive/description/?envType=study-plan-v2&envId=top-100-liked
题目描述:
思路一:
核心思想: 我们可以使用一个哈希集合(set)来记录nums中所有存在的整数,然后从 1 开始依次检查这些数字是否存在于集合中,一旦发现某个数字不存在,就返回它。
这种方法虽然没有达到最优的空间复杂度(O(1)),但它的实现简单、逻辑清晰,适用于大多数场景,特别是对时间和思维成本有限制的情况下。
具体实现步骤:
第一步: 使用 set()
构建哈希集合
hashmap = set(nums)
将原始数组转换成一个集合,这样做的好处是:
- 去除重复元素。
- 查询的时间复杂度降为 O(1)。
第二步: 从 1 开始查找第一个不在集合中的正整数
min_positive_num = 1
while min_positive_num in hashmap:min_positive_num += 1
我们从 1 开始不断递增,直到找到一个不在集合中的数字为止。
这个循环最多执行 n + 1 次(假设所有正整数都连续存在),因此时间复杂度是 O(n)。
⏱️ 时间与空间复杂度分析
时间复杂度:O(n),因为构建集合需要 O(n),查找过程最多遍历 n+1 个数字;
空间复杂度:O(n),因为使用了一个哈希集合保存所有不重复的元素。
✅ 优点:逻辑清晰、易于理解、编码简单
❌ 缺点:没有做到 O(1) 的空间复杂度,不满足题目中的常数级别要求,需要优化。
代码如下:
class Solution(object):def firstMissingPositive(self, nums):""":type nums: List[int]:rtype: int"""hashmap = set(nums)min_positive_num = 1while min_positive_num in hashmap:min_positive_num += 1return min_positive_num
执行时间如下:
思路二:
🧠 怎样可以做到 O(1) 空间?
题目中要求我们找出 最小的未出现的正整数。观察可知:
- 如果数组长度是 n,那么答案一定在 [1, n+1] 范围内。
- 比如 [1,2,3] 缺失的是 4;
- 比如 [1,2,4] 缺失的是 3;
- 所以只要我们能判断出 [1, n] 中哪些数字出现了,就能找到第一个没出现的。
那我们就可以尝试把每个数字放到它“应该在”的位置上:
- 数字 1 放到索引 0
- 数字 2 放到索引 1
- …
- 数字 x 放到索引 x - 1
这样处理后,再次遍历数组,如果某个位置上的数字不是它“应该有的”,就说明这个数字缺失了。
通过 原地哈希 的方式,我们可以将原本需要哈希表或集合辅助的方法优化到 常量级空间复杂度 O(1)。这种思想的核心在于:将原始数组作为隐式的哈希表来使用,把每个数字放到它应该出现的位置上(如数字 x 放到索引 x - 1 处),这样就能通过一次遍历找到缺失的最小正整数。
具体实现步骤:
第一步:将每个数放到其“正确”的位置
for i in range(nums_len):while 1 <= nums[i] <= nums_len and nums[nums[i] - 1] != nums[i]:correct_index = nums[i] - 1nums[correct_index], nums[i] = nums[i], nums[correct_index]
这段代码的作用是:
- 如果当前数字
nums[i]
在合法范围内 [1, nums_len],并且不在它应该在的位置nums[nums[i] - 1]
上,就把它交换到对应的位置上去。
举个例子:
- 当前元素是 3,而 nums[2] 不等于 3,就交换。
- 这样不断交换,直到当前位置上的元素已经在正确的位置上为止。
⚠️ 注意:这个循环必须使用 while,不能用 if,因为每次交换可能只移动了一个元素,还需要继续检查新的 nums[i] 是否也需要调整。
第二步:查找第一个不符合期望的位置
for i in range(nums_len):if nums[i] != i + 1:return i + 1
此时,如果某个位置 i 上的值不是 i + 1,说明这个正整数缺失,返回即可。
第三步:所有位置都符合预期,则返回 n + 1
return nums_len + 1
表示从 1 到 n 都存在,那么最小缺失的就是 n + 1。
⏱️ 时间与空间复杂度分析
时间复杂度:O(n) ,因为每个元素最多被交换一次。
空间复杂度:O(1) ,原地操作,没有使用额外数据结构。
代码如下:
class Solution(object):def firstMissingPositive(self, nums):""":type nums: List[int]:rtype: int"""nums_len = len(nums)for i in range(nums_len):while 1 <= nums[i] <= nums_len and nums[nums[i] - 1] != nums[i]:correct_index = nums[i] - 1nums[correct_index], nums[i] = nums[i], nums[correct_index]for i in range(nums_len):if nums[i] != i + 1:return i + 1return nums_len + 1
执行时间如下,空间复杂度得到了明显改善(25.66MB -> 19.87MB):