41. 缺失的第一个正数
题目描述
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
进阶:你可以实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案吗?
示例1:
输入:nums = [1,2,0]
输出:3
示例2:
输入:nums = [3,4,-1,1]
输出:2
示例3:
输入:nums = [7,8,9,11,12]
输出:1
提示:
- 0 ≤ n u m s . l e n g t h ≤ 300 0 \le nums.length \le 300 0≤nums.length≤300
- − 2 31 ≤ n u m s [ i ] ≤ 2 31 − 1 -2^{31} \le nums[i] \le 2^{31} - 1 −231≤nums[i]≤231−1
题解:
法一:
核心思想:
理想情况下,长度为 n
的 nums
应该正好包含 1~n
,如果某个元素 nums[i]
不在范围内,那么在理想情况下,nums
包含的范围应该相应的减小。数组的长度和元素范围是相对应的。
-
使用两个变量
l
和r
:-
l
表示遍历到目前为止,nums
已经包含的正整数范围是[1,l]
,开始时l = 0
,表示没有包含任何正整数 -
r
表示遍历到目前为止,后续出现最优状况的情况下,nums
可能包含的正整数范围是[1,r]
,开始时r = n
,因为还没开始遍历,所有以后续出现的最优状况是num
包含1~n
所有的整数。r
同时表示nums
当前的结束位置。
-
-
从左往右遍历
nums
,遍历到位置l
,此时已经包含的正整数范围是[1, l]
,而在后续最优的情况下,nums
可能包含的正整数范围是[1, r]
,所以当前需要的元素范围是[l + 1, r]
,分情况讨论:- 若
nums[l] == l + 1
,说明此时nums
包含的正整数范围可以扩到[1, l + 1]
,所以l++
- 若
nums[l] <= l
,此时说明[l + 1, r]
范围上的数少了一个,所以在nums
后续最优的情况下,可能包含的正整数范围缩小为[1, r - 1]
,此时把nums[r - 1]
位置上的数放到位置l
上,下一步检查这个元素,并且r--
。 - 若
nums[l] > r
,与 2 同理 - 若
num[num[l] - 1] == nums[l]
,在步骤 2 和 3 中没中,说明nums[l]
在[l + 1, r]
范围内,而且这个数应该在nums[l] - 1
位置上,而此时出现num[num[l] - 1] == nums[l]
,说明出现了两个nums[l]
,既然[l + 1, r]
上出现了重复元素,说明[l + 1, r]
范围上少了一个数,操作同步骤 2 和 3 - 若步骤 2、3、4 都没中,说明发现了
[l + 1, r]
范围上的数,并且此时未发现重复,则nums[l]
应该放在nums[l] - 1
位置上,交换l
和nums[l] - 1
位置上的元素,继续判断l
位置上的元素
- 若
-
最终
l
和r
会碰在一起,nums
已经包含的正整数范围是[1, l]
,返回l + 1
即可
法一代码:
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int l = 0, r = nums.size();
while ( l < r ) {
if ( nums[l] == l + 1 ) ++l;
else if ( nums[l] <= l || nums[l] > r || nums[nums[l] - 1] == nums[l] )
swap(nums[l], nums[--r] );
else swap( nums[l], nums[nums[l] - 1]);
}
return l + 1;
}
};
/*
时间:0ms,击败:100.00%
内存:9.4MB,击败:97.55%
*/
法二:
假设 nums
长度为 n
,理想的情况是 nums
的所有元素正好包括 1~n
并且 nums[i] == i + 1
,这种情况我们很容易知道未出现的最小正整数是什么。
但是仅仅限于 理想 情况。虽然元素是无序的,但我们知道了什么情况下最容易找到未出现的最小正整数,我们可以向这种 理想 情况转换,具体操作:若 1 <= nums[i] <= n
且 nums[i] != nums[nums[i] - 1]
,我们应该将其放到 nums[nums[i] - 1]
位置。假设所有 1~n
范围内的元素都放在正确的位置,剩下的就是从前往后遍历找到第一个 nums[i] != i + 1
的位置,i + 1
就是我们想要的答案。
法二代码:
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for( int i = 0; i < n; ++i ) {
while ( nums[i] >= 1 && nums[i] <= n && nums[i] != nums[nums[i] - 1] )
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;
}
};
/*
时间:0ms,击败:100.00%
内存:9.3MB,击败:99.55%
*/
两种方法均用到了一个关键思想 将 nums[i]
放到其应该待的地 。