【LeetCode-数组】缺失的第一个正数

题目描述

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

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

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

输入: [7,8,9,11,12]
输出: 1

说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。
题目链接: https://leetcode-cn.com/problems/first-missing-positive/
做这一题之前,可以先做一下找到所有数组中消失的数字(思路2)

思路

使用哈希表来做,题目要求常数级别的额外空间,所以不能额外定义哈希表,但我们可以就在输入数组上构建哈希表。遍历数组,假设当前的元素为 nums[i],由于是找出 [1, n] 之间未出现的最小整数,所以,我们将 nums[abs(nums[i])-1] 设为负数,使用 abs(nums[i]) 的原因是因为 nums[i] 可能在之前的遍历过程中以及被设为负数了,但下标是一个大于等于 0 的数字,所以需要取绝对值。例如,假设 nums[1] = 3,nums[2] = 6,那么遍历到 nums[1] 时,我们将 nums[2] 设为 -6(nums[nums[1]-1] = -nums[nums[1]-1]),代表 2+1=3 出现过。当遍历结束时,我们再遍历一遍 nums,如果 nums[i]>=0 的话,说明 i+1 没有在 nums 中出现,返回 i+1。

还有一个问题,就是输入的 nums 中可能包含负数。比如,nums 中包含 -1 但不包含 1,那么我们将 nums[abs(-1)-1] 设为了负数,表明 1 出现了,但实际上 1 并没有出现。这个问题的解决办法是:我们首先对输出数组 nums 遍历一遍,将其中的负数变为 0,然后在将 nums[abs(nums[i])-1] 设为负数的过程中,如果 nums[abs(nums[i])-1]==0,我们就将 nums[abs(nums[i])-1] 设为 -(num.size()+1),这样的话,在后序的过程中,nums[abs(nums[i])-1] 不会对其他的元素产生影响。具体代码如下:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        for(int i=0; i<nums.size(); i++){ // 将负数转为0
            if(nums[i]<0) nums[i]=0;
        }

        for(int i=0; i<nums.size(); i++){ // 将nums[abs(nums[i])-1]设为-1
            int idx = abs(nums[i])-1;
            if(idx>=0 && idx<nums.size()){
                if(nums[idx]==0) nums[idx]=-nums.size()-1;
                else nums[idx] = -abs(nums[idx]); //这样可以保证一个数nums[i]出现多次时,nums[abs(nums[i])-1]一直为负数
            }
        }

        for(int i=0; i<nums.size(); i++){
            if(nums[i]>=0) return i+1;
        }
        return nums.size()+1;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

还可以这样写:
先将小于等于 0 的元素转为数组中出现的任意一个正数,然后求解。代码如下:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        if(nums.empty()) return 1;

        int t = 0;  // 任意的一个正数
        for(int i=0; i<nums.size(); i++){
            if(nums[i]>0){
                t = nums[i];
                break;
            }
        }
        if(t<=0) return 1;

        for(int i=0; i<nums.size(); i++){
            if(nums[i]<=0) nums[i]=t;  // 将小于等于0的数转为t
        }

        for(int i=0; i<nums.size(); i++){
            if(abs(nums[i])-1>=0 && abs(nums[i])-1<nums.size()){
                nums[abs(nums[i])-1] = -abs(nums[abs(nums[i])-1]);
            }
        }

        for(int i=0; i<nums.size(); i++){
            if(nums[i]>=0) return i+1;
        }
        return nums.size()+1;
    }
};

这种写法个人感觉更容易理解,时间复杂度也是 O(n)。

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

相关题目

1、找到所有数组中消失的数字:https://www.cnblogs.com/flix/p/12857693.html

原文地址:https://www.cnblogs.com/flix/p/12969036.html