“
给你一个未排序的整数数组
nums
,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为
O(n)
并且只使用常数级别额外空间的解决方案。”
看到时间复杂度为O(n),立刻想到:最后应该是要遍历一次一个数组或者表之类的东西来得到答案,且在这个数组(表)的每个位置都只是看一下“值”就走,不能逗留
看到空间复杂度为常数级别,排除“将数组中的元素一个个加进哈希表”的想法
那怎么办?
只能对原数组修改,最后看的应该也是修改后的原数组。
可是怎么修改呢?我第一个想法是遍历每一个元素a,如果1<=a<=n,就在数组下标[a-1]处+1。可是数组是int数组,每个位置只能存一个值。所以这个想法破产了
不过我们要记录的只是这个整数 “有没有出现过” ,而不是 “出现过几次” 。那负号是否可行?第一次遇到这个元素时,给nums[a-1]加一个负号,之后再遇到就不动了,最后看出现正数的最小下标是多少就得到答案了。
可是nums[a]可能本来就是正数啊。没关系,把负数变成n+1就行,反正不管碰到负数,还是n+1,都不会加负号。
int firstMissPositive(vector<int>& nums){
int n=nums.size();
for(int& num:nums){//这里是引用,所以修改num,就相当于修改nums的这一个元素了
if(num<=0){
num=n+1;
}
}
for(int i=0;i<n;++i){
int num=abs(nums[i]);
if(num<=n){//也就是看原来的那个元素是不是1~n的正整数
nums[num-1]=-abs(nums[num-1]);
}
}
for(int i=0;i<n;++i){//最后遍历一次数组,得到答案
if(nums[i]>0){
return i+1;
}
}
return n+1;
}
官方题解里面,还给了另一种解法:把所有1~n的整数a移到nums[a-1]上,最后只要遍历一下数组,找出第一个不符合 “值是下标+1” 的元素 i 就行
若nums[a-1]已经是a,那就别动了
因为最多n个1~n的整数,所以最多交换n次
力扣代码如下:
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[nums[i] - 1], nums[i]);//nums[i]是a,它应该在nums[a-1]处
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] != i + 1) {
return i + 1;
}
}
return n + 1;
}
};