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 = 1

        while min_positive_num in hashmap:
            min_positive_num += 1
        
        return 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] - 1
        nums[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] - 1
                nums[correct_index], nums[i] = nums[i], nums[correct_index]
        
        for i in range(nums_len):
            if nums[i] != i + 1:
                return i + 1
        
        return nums_len + 1

执行时间如下,空间复杂度得到了明显改善(25.66MB -> 19.87MB):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值