题目:给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
输入: [1,2,0]
输出: 3
输入: [3,4,-1,1]
输出: 2
输入: [7,8,9,11,12]
输出: 1
代码:
class Solution {
public:
int firstMissingPositive(vector<int> A) {
int i,j;
int n=A.size();
for(i=0;i<n;i++){
int cur=A[i];
// if in place or non-pos or out of bounds, skip.
if(cur==i+1||cur<=0||cur>n)continue;
swap(A[i],A[cur-1]);
// if not the same, then reprocess it.
if(A[i]!=A[cur-1])
i--;
}
for(i=0;i<n;i++)
if(A[i]!=i+1)
return i+1;
return n+1;
}
};
具体来分析一下这个代码:
乍一看,if(cur==i+1||cur<=0||cur>n)continue;似乎没什么大用处,但这句话配合swap其实发挥了至关重要的作用。另外判断A[i]和A[cur-1]看起来很奇怪,不过这其实是为了避免无限循环,一直交换。在排除了 负数、大于范围的数、重复的数、以及合适的数(正确位置的数)之后,那么交换过来的就只可能是不合适的数,这时就要一直循环交换指定位置的数字了。
算法思想是:
1.扫描一遍数组,得到当前索引和当前元素,如果索引和元素不相同,也就是这个元素出现在了不合适的位置,就交换它,交换的对象是这个元素值应该出现的位置。
2. 当我把应该出现的位置的元素调换过来之后,会有什么情况呢?
a. 如果交换来的数字是正确的数字,就跳过这个i了,进行下一个i
b. 如果交换过来的数字是负数,那就让这个负数来占这个位置的坑,如果大于了应该的范围n,也来占坑,就进行下一个i
c. 如果交换过来的数字和自己相同的话,就说明这会陷入无限的交换之后,我们要阻止这种情况的发生,所以也进行下一个i
d. 如果这个数字范围不出格,而且也和之前不同的话,就可以在这个索引i位置上一直交换下去,直到出现a.b.c的情况
3.当我们这样扫描一遍数组之后,就再扫描一遍数组,找出第一个位置与元素对不上的数字,得到结果
最后:如果题目问找到第一个缺失的非负数的话,就可以改写代码为(注意有两点要改,一是和谁交换,二是判断某一个元素的是否在合理范围内的这个范围变更了):
int firstMissingNotMinus(vector<int> A)
{
for (int i = 0; i < A.size(); i++)
{
int cur = A[i];
if (cur == i ||cur <0 || cur >=A.size()) continue;
swap(A[i], A[cur]);
if (A[i] != A[cur])
i--;
}
for (int i = 0; i < A.size(); i++)
{
if (A[i] != i)
return i;
}
return A.size();
}
延申:
这个题跟剑指offer第三题很像,不过剑指offer第三题限制了数据元素范围,不再考虑负数、超范围的数,且直接求出重复的数字,因此相较于这个题目简单很多。