17.缺失的第一个正数

109 阅读2分钟

题目链接

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

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

示例 1:

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

输出:3

解释:范围 [1,2] 中的数字都在数组中。

解法1 set辅助

思路

在不考虑辅助空间的情况下,要想找出最小的正整数,可以把所有的数字放入 set 中。然后从 1 开始遍历 set,如果存在,那么遍历自增,直到 set 不包含这个数。

代码

function firstMissingPositive(nums: number[]): number {
    const hashSet: Set<number> = new Set(nums);

    let missing = 1;
    while (hashSet.has(missing)) {
        missing += 1;
    }

    return missing;
};

时空复杂度分析

时间复杂度:在生成 set 时顶多遍历一遍,总共两次遍历,所以复杂度为 O(n)

空间复杂度:有 set 的额外辅助空间 O(n)

解法2 原地哈希

思路

上一种解法使用了额外的空间,要想不使用额外空间,可以将这个数组本身抽象成一个 set,让数组本身呆在对应的下标。比如数字 1 应该在索引 0 的位置,数字 2 应该在索引 1 的位置,以此类推。再次遍历即可找出最小的正整数。

代码

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

    for (let i = 0; i < n; i++) {
        while (
            nums[i] > 0 && // 首先是正整数
            nums[i] <= n && 
            // 其次要在数组长度里。如果特别大,那么没必要换
            // 因为找的是最小的数,即使全部遍历完毕都存在,那么最小的其实是 n + 1
            nums[i] != nums[nums[i] - 1]
            // nums[i] - 1代表了下标i的数,减一是因为从1放在0的下标(寻找正整数。从1开始)
            // 检查当前数字 nums[i] 是否在正确位置,即下标为 nums[i] - 1 的位置
        ) {
            let temp:number = nums[nums[i] - 1];
            nums[nums[i] - 1] = nums[i];
            nums[i] = temp;
        }
    }

    for (let i = 0; i < n; i++) {
        if (nums[i] != i + 1) {
            return i + 1;
        }
    }

    return n + 1;
};

时空复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)