LeetCode热题(JS版) - 41. 缺失的第一个正数

91 阅读1分钟

题目

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

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

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

示例 2:

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

示例 3:

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

提示:

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

思路

解题思路

首先,我们可以发现,答案肯定是在[1,nums.length+1]这个区间内。

我们可以用一个数组arr来记录每个数是否出现过,做法如下:

  • 将所有小于等于0的数变为nums.length+1,因为小于等于0的数一定不是我们要找的最小正整数
  • 遍历一遍nums,如果当前数在[1,nums.length+1]区间内,那么将arr[nums[i]-1]置为true
  • 再遍历一遍arr,如果当前位置为false,那么返回当前位置+1

代码实现

function firstMissingPositive(nums: number[]): number {
    const n = nums.length;

    // 要找的是正数,所以<=0的就不需要。将所有<=0的数标记为数组范围之外的数n+1
    for(let i = 0; i < n; i++) {
        if(nums[i] <= 0) {
            nums[i] = n + 1;
        }
    }

    // 遍历一遍,在范围之内的也就是出现过的数,标记为负数
    for(let i = 0; i < n; i++) {
        const num = Math.abs(nums[i]);// 取绝对值是因为,有些数可能重复出现
        if(num <= n) {
            nums[num - 1] = - Math.abs(nums[num - 1])
        }
    }

    // 再遍历一遍,如果不是负数就说明没有出现过,顺序遍历,所以是最小的数
    for(let i = 0; i < n; i++) {
        if(nums[i] > 0) {
            return i + 1;// 最小的
        }
    }

    return n+1;// 都不存在,那就是最外面一个了
};
  • 时间复杂度:O(n),其中n为数组的长度,我们需要遍历两次数组。
  • 空间复杂度:O(1),我们只需要常数的空间来存储变量。

image.png 总结

本题的难点在于如何使用常数的空间来解决问题,我们可以使用原数组来存储每个数字是否出现过,从而避免使用额外的空间。

对于数组中的数字,我们可以将其对应的位置取相反数来标记其是否出现过,这样既不会影响原数组的元素又可以标记每个数字是否出现过。

最后,如果1~n都出现了,那么答案就是n+1。