这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
示例 2:
输入:nums = [3,4,-1,1]
输出:2
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
链接:leetcode-cn.com/problems/fi…
题解
- 可以先从简单的方案说起. 可以想到的是利用 hash 的方法, 将 nums 中的元素作为 hash 的下标, 出现在 nums 中的元素, 在 hash 对应下标均为 1. 这样, 将所以的出现过的元素都用 hash 数组标识了, 我们可以从位置 1 开始检查对应位置上的 hash 是否为 1, 一旦发现未出现或者遍历到 hash 数组的尾部了, 那么就可以找出对应的值了. 具体代码见下方, 虽然本方法的时间复杂度为 O(n), 但是空间复杂度为 O(max(nums)), 因此并不满足题目的要求.
/**
* @param {number[]} nums
* @return {number}
*/
var firstMissingPositive = function (nums) {
let hash = [];
for (let n of nums) {
if (n >= 0) {
hash[n] = 1;
}
}
let res = 1;
let i = 1;
while (i < hash.length) {
if (!hash[i]) {
res = i;
break;
}
i++;
}
if (i === hash.length) {
res = hash.length;
}
return res;
};
- 方法1中, 我们利用 hash 的方法来达到时间复杂度为 O(n) 的目的, 但是空间复杂度不是常数, 有没有更好的方法来减少空间复杂度呢? 聪明的你可能已经想到了, 我们可以复用 nums 这个数组而不必开辟新的 hash 数组, 怎么做呢.
首先我们要确定的是, 最终要返回的正整数一定是在 [1, n+1] 的区间内, n 为 nums 的长度, 这是要明确的. 在确定好这一点之后就好办了, 其实只要我们把每个元素放在跟他对应的位置上, 然后从头检查每个位置上的元素和下标是不是对应的就好. 具体来说就是, 比如有元素 1, 就把 1 放在 0 位置上, 元素 5 就把 5 放在 4 位置上, 即数组中元素是下标加一, 这样调整过后, 当位置 i 上的元素不是 i + 1 时, i + 1 就是最后的答案, 如果所有的元素都是对应的, 那就返回 n + 1. 在调整过程中我们可能会遇到小于 1 或者大于 n 的数, 这时候跳过这个位置就好. 具体方法可以见下面的代码. 我们可以称本方法为原地hash, 时间复杂度为 O(n), 空间复杂度为 O(1).
/**
* @param {number[]} nums
* @return {number}
*/
var firstMissingPositive = function(nums) {
const len = nums.length
const swap = (a, b) => {
let tmp = nums[a]
nums[a] = nums[b]
nums[b] = tmp
}
for (let i = 0; i < len; ++i) {
// 调整 i 位置的元素, 直到符合要求或者超出范围
while (nums[i] > 0 && nums[i] < len + 1 && nums[nums[i] - 1] !== nums[i]) {
swap(i, nums[i] - 1)
}
}
for (let i = 0; i < len; ++i) {
if (nums[i] !== i + 1) {
return i + 1
}
}
return len + 1
};