给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0] 输出:3 解释:范围 [1,2] 中的数字都在数组中。
示例 2:
输入:nums = [3,4,-1,1] 输出:2 解释:1 在数组中,但 2 没有。
示例 3:
输入:nums = [7,8,9,11,12] 输出:1 解释:最小的正数 1 没有出现。
提示:
1 <= nums.length <= 105
-2^31 <= nums[i] <= 2^31 - 1
如果没有时间、空间复杂度要求,那么可以使用哈希表存储数组中出现的每一个元素,然后从正整数1开始枚举,直到找到不在表内的正整数即为所求。
但由于限制了时间复杂度O(n),使用常数级别的额外空间。也就是要求每个元素只遍历一次,且不能创建长度为n的数据结构。
所以使用考虑使用原地哈希。将哈希表存储在要哈希的数组中。
对于一个长度为 N 的数组,其没有出现的最小正整数只能在 [1,N+1] 中。
因此我们想要构建的目标数组形式如下:
- 以[3,-1,4,1]为例。需要通过变换,让它变成[1,-1,3,4]的形式。
- 考虑下标,nums[i]存储的值,就是i+1。而转换后第一个不满足这个条件的位置,就是缺少的第一个正整数。
第一步:i = 0,nums[i] = 3 != i+1,因此需要将nums[i]的值与nums[nums[i] - 1]交换。
第二步,i 仍然为0,因为当前位置没有被交换为1。继续交换。
此时,i = 0位置上的数已经符合要求了,i++。
这里如果起始数组里没有1这个数字,也就是说缺失的就是1会发生什么?
--- 在经过多次循环交换后,要么这个位置的值小于0,要么大于n+1,要么像后文所述,出现了重复数字。
所以要注意循环跳出条件。
这个例子中,到这一步其实就已经完成交换了,接下来就是遍历完所有的i,结束。
代码:
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
for(int i = 0;i < n;i++){
while(nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]){
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;
}
};
额外关注下nums[nums[i] - 1] != nums[i]这个循环跳出条件。
由于题目不保证一个数只出现一次,如果交换的两个数恰好相等,那么就会陷入死循环,所以加入这个条件跳出循环。