力扣 41. 缺失的第一个正数 有意思

41. 缺失的第一个正数

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

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

示例 1:

输入:nums = [1,2,0]
输出:3

示例 2:

输入:nums = [3,4,-1,1]
输出:2

提示:

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

法一:标记

思路来自官方题解

对于一个数组,长度为N,没有出现的最小正整数只能在[1,N+1]

这是因为如果 [1, N]都出现了,那么答案是,否则答案是[1, N]中没有出现的最小正整数。

先将所以不大于0的元素都赋值为N+1,接下来只处理正整数;

将值在[1,N]的元素放入标记数组中来记录,因为此标记数组长度N和原数组长度N一样,所以可以合并(原数组既保存数组信息,同时也标记哪些值已经出现过)

遍历数组,当前元素num,如果 num∈[1,N],在数组上做标记,将num-1(下标0开始)的元素赋值为其原值的绝对值的负,

如nums=[3,1,5],遍历到num=1,第2个元素的值是1,nums[1-1]=num[0]=-3,-3的符号表示此元素下标(0)已经被标记出现过,即有元素的值为0+1=1,原数组里面已经出现过1;

遍历打标记完成后,再次遍历寻找不为负数的元素,那么这个元素的下标+1就是还没有出现过的最小正整数,如果找不到,表示1到N都出现过,那么答案为N+1

查看代码
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n=nums.size();
        //小于0的修改为N+1
        for(int& num:nums){
            if(num<=0){
                num=n+1;
            }
        }
        //打标记
        for(int i=0;i<n;i++){
            int num=abs(nums[i]);
            if(num<=n){
                nums[num-1]=-abs(nums[num-1]);//当前位置的前一个位置标记在数组中
            }
        }
        for(int i=0;i<n;i++){
            if(nums[i]>0){
                return i+1;
            }
        }
        return n+1;
    }
};

法二:置换

来自官方题解

除了打标记以外,我们还可以使用置换的方法,将给定的数组「恢复」成下面的形式:

如果数组中包含 x∈[1,N],那么恢复后,数组的第x−1 个元素为 x。

在恢复后,数组应当有 [1, 2, ..., N] 的形式,但其中有若干个位置上的数是错误的,每一个错误的位置就代表了一个缺失的正数。以题目中的示例二 [3, 4, -1, 1] 为例,恢复后的数组应当为 [1, -1, 3, 4],我们就可以知道缺失的数为 2。

那么我们如何将数组进行恢复呢?我们可以对数组进行一次遍历,对于遍历到的数 x=nums[i],如果 x∈[1,N],我们就知道 x 应当出现在数组中的 x−1 的位置,因此交换 nums[i] 和 nums[x−1],这样 x 就出现在了正确的位置。在完成交换后nums[i] 可能还在 [1, N]的范围内,我们需要继续进行交换操作,直到 x不属于[1,N]。

注意到上面的方法可能会陷入死循环。如果nums[i] 恰好与 nums[x−1] 相等,那么就会无限交换下去。此时我们有nums[i]=x=nums[x−1],说明 x 已经出现在了正确的位置。因此我们可以跳出循环,开始遍历下一个数。

查看代码

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n=nums.size();
        for(int i=0;i<n;i++){
            while(nums[i]>0&&nums[i]<n&&nums[nums[i]-1]!=nums[i]){
                swap(nums[i],nums[nums[i]-1]);
            }
        }
        for(int i=0;i<n;i++){
            if(nums[i]!=i+1)
                return i+1;
        }
        return n+1;
    }
};
posted @ 2022-03-23 16:21  付玬熙  阅读(31)  评论(0编辑  收藏  举报