算法:最小未出现的正整数
问题描述:给定一个未排序的数组,找出其中没有出现的最小正整数(num>=1)
示例1:
> 输入:[1,2,0]
> 输出:3
示例2:
>输入:[3,4,-1,1]
>输出:2
示例3:
>输入:[7,8,9,10]
>输出:1
-
说明:本题描述虽然十分简单,但是想要做到最好的效果并不容易想到,我们可以从时间复杂度和空间复杂度上面要求: 1.时间复杂度为O(n) 2.空间复杂度为O(1) 首先我们需要分析一个给定长度n的数组,所求的数最大为n+1,最小为1;所以我们可以得出几个结论:
- 1.当arr[i]>n的时候,arr[i]+1>n+1,必然不会是所求的结果,所以我们可以直接舍弃这个元素
- 2.当arr[i]<0的时候,arr[i]+1<1,必然不会是所求的结果,所以我们也可以直接舍弃这个元素
假设我们现在只考虑时间复杂度为O(n)的情况,那么我们可以如下考虑
可以定义一个临时的数组temp,大小为n,令他所有元素为0;遍历原数组arr,当arr[i]>0&&arr[i]<=n,temp[arr[i]]=1 。
最后我们可以得出temp数组下标最小的0元素的下标i就是我们所求的最>小未出现正整数i+1
class solution{
public :
int firstMissingPositive(vector<int>& nums){
int size = noms.size();
int *temp = new int[size];
for(int i=0; i<size;i++){//初始化
temp[I]=0;
}
for(int i=0; i<size;i++){//判断
temp[i]=0;
if(nums[i]>0&&nums[i]<=size){
temp[i]=1;
}
}
for(int j =0;j<size;j++){//得出结果
if(temp[j]==0)
return j+1;
}
return 0;
}
}
现在我们再加大难度,考虑时间复杂度为O(n),并且空间复杂度限制在O(1)的情况
在此之前,我们可以先考虑下面两种简化情况:
1.给定一个数组a,长度为n,除了a[0]以外,其他元素都有a[i]==i,请找出第一个大于0并且不在这个数组里面的正整数
解答:n+(a[0]==n);当a==NULL,return 1
注意:必须考虑只有一个元素a[0]的情况,和元素为空的情况。
2.给定一个数组,长度为n,存在K个元素使得a[i]<0或者a[i]>n,其他元素a[i]==i,请找出第一个大于0并且不在这个数组里面的正整数
情况1:当k==0,也就是类似于问题1,
情况2:当k!=0,扫描完一遍数组,当第一个出现i!=a[i]的时候,i就是答案:
if(n<=0)return 1; //判断数组是否为空
for(int i=0;i<n;i++){//数组不为空,开始循环查找i!=a[i]
if(I!=a[i]){
return i;
}
}
return n+(a[0]==n);//查找结束,回到问题1
经过上面两道小问题之后,我们可以发现原本的问题是可以转化到上面这两道题的
首先,我们遍历数组,将所有0 < a[i] < n 的元素都交换到对应的下标中,即a[i]==i.
然后,就转化到了问题2
代码如下
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int size = nums.size();
if(size <=0) return 1;
/*巧妙之处在这里,看似双重循环,其实只遍历了一遍数组,因为在内层交换过后,存在一部分i==a[i],当外层遍历到该元素,就不会进入循环内,相当于只遍历了一遍数组。*/
for(int i=0;i<size;i++){
while(nums[i]>0&&nums[i]<size&&(i!=nums[i])){
int t = nums[i];
if(nums[t]==nums[i]) break;
int tmp = nums[i];
nums[i]=nums[t];
nums[t] = tmp;
}
}
for(int i =1;i<size;i++){
if(i!=nums[i])return i;
}
return size+(nums[0]==(size));
}
};
由于我们数组下标是从0开始的,所以nums[0]是经常作为临界条件出现的,我们需要判断nums[0]是否等于数组长度的情况