41. 缺失的第一个正数

题目

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。
示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。
示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

提示:

1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1

答案

class Solution {  
public:  
    int firstMissingPositive(vector<int>& nums) {  
        int n = nums.size();  
          
        // 第一步:将所有非正数替换为n+1(或任何大于n的值),这样它们就不会干扰后续的逻辑  
        for (int i = 0; i < n; ++i) {  
            if (nums[i] <= 0 || nums[i] > n) {  
                nums[i] = n + 1;  
            }  
        }  
          
        // 第二步:遍历数组,将每个元素abs值对应的索引位置上的数变为负数(标记为已出现)  
        for (int i = 0; i < n; ++i) {  
            int index = abs(nums[i]) - 1;  
            if (index < n && nums[index] > 0) {  
                nums[index] = -nums[index];  
            }  
        }  
          
        // 第三步:找到第一个未被标记为正数的索引(即仍为正数的索引)+1  
        for (int i = 0; i < n; ++i) {  
            if (nums[i] > 0) {  
                return i + 1;  
            }  
        }  
          
        // 如果所有正数都已出现,则返回n+1  
        return n + 1;  
    }  
};

算法原理

  1. 清理无效元素
    遍历数组nums,将所有非正数(即小于或等于0的数)以及大于数组长度n的数替换为一个特定的值(这里选择n+1),这个值在后续步骤中不会被用作索引,因此不会干扰算法的逻辑。这一步的目的是确保后续步骤中,只有有效的正整数索引会被考虑。

  2. 标记已出现的正整数
    再次遍历数组nums,对于每个元素nums[i](此时它应该是一个正整数或n+1),我们取其绝对值并减去1,得到一个索引index。这个索引对应的是我们想要标记的正整数(即index + 1)。然后,我们检查nums[index]的值,如果它是正数(即未被标记过),则将其变为负数,表示该正整数(index + 1)已经出现过。这一步利用了数组索引和正整数之间的对应关系,通过修改数组元素的值来记录哪些正整数是存在的。

  3. 寻找缺失的正整数
    最后,我们再次遍历数组nums,寻找第一个未被标记为正数的索引i。由于数组索引从0开始,而题目要求的是正整数,所以我们返回i + 1作为第一个缺失的正整数。如果遍历完整个数组都没有找到未被标记为正数的索引,说明从1到n的所有正整数都已经在数组中出现过,那么缺失的正整数就是n+1。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值