First Missing Positive
Given an unsorted integer array, find the first missing positive integer.
For example,
Given [1,2,0]
return 3
,
and [3,4,-1,1]
return 2
.
Your algorithm should run in O(n) time and uses constant space.
思路:
要求用O(n)的时间想到用hash,又要求用constant space就考虑使用原数组的空间来实现hash。
其中一种思路是把A[i]映射到A[A[i]-1]上,把A[A[i]-1]赋值为负表示A[i]已被映射。最后从前至后扫描一遍A,找到第一个大于零的A[i],结果就是i+1。
需要注意的是A[i]小于等于零,以及A[i]大于数组大小的情况。
以下是参考博客(http://blog.csdn.net/pickless/article/details/9234301):
O(n^2)的算法是最直观的思路,枚举1~n,然后检查是否存在。
O(nlogn)的算法也不难想到,对数组排序,然后遍历数组。
如果允许O(n)的空间,利用一个hash数组,也能够实现O(n)的算法。
现在题目给予的是常量空间,要求实现O(n)的算法。
网上很多同学的blog都给出了基于hash的思想的一个O(n)的算法。
大致思路:
既然不允许使用额外的数组作为hash数组,那么就考虑让原数组多带点信息。
第一步,假设原数组都是正数,那么我们可以考虑将相应位置的正数变成负数来表示该位置已经出现过。听上去有些抽象,举个例子:
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 1 4 5 8 4
由于数组下标都是从0到n-1,而我们要考虑的是1到n,所以要从[0, n-1]映射到[1, n]。
|A[0]| (绝对值符号) = 9,所以我们将A[9 - 1([1,n]映射到[0, n-1])] 即A[8]置为-A[8]。
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 1 4 5 -8 4
|A[1]| = 7,A[7-1]即A[6]=-A[6]。
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 1 -4 5 -8 4
|A[2]|=6,A[5]=-A[5]。
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 -1 -4 5 -8 4
最终:
0 1 2 3 4 5 6 7 8 9
A = -9 7 -6 -5 -3 -1 -4 -5 -8 4
第二步,很简单了,遍历数组A,看哪一位不是负数,则该位下标+1则是所求。
对上例,A[1]=7,所以First Missing Positive是2。
假设存在负数和0,则可以将这些全部置为一个无穷大(或者N+1)的正数即可。在置反时,如果A[i]超出了范围,则不理会。
例如:
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 1 4 -5 8 4
预处理后变为:
0 1 2 3 4 5 6 7 8 9
A = 9 7 6 5 3 1 4 11(N+1) 8 4
最终:
0 1 2 3 4 5 6 7 8 9
A = -9 7 -6 -5 -3 -1 -4 -11 -8 4
我采用了另外一种方法。
大致思路:
第一步,遍历数组,如果A[i]不等于i+1,也就是相应数字没有出现在该出现的位置上,则交换A[i]和A[A[i] - 1],直到A[i]<=0或者A[i]>N或者A[i]==i+1为止。
第二步,遍历数组,第一个满足A[i] != i+1的i,First missing positive为i+1。
举例说明:
0 1 2
A = 0 1 3
A[0] = 0,满足A[i]<=0的条件,不作变动。
A[1] = 1,交换A[1]和A[A[1] - 1]即A[0],得到:
0 1 2
A = 1 0 3
A[2] = 3,满足A[i] == i + 1的条件,不作变动。
遍历数组,发现A[1] != 2,因此First Missing Positive为2.
P.S.
0 1
A = 1 1
会陷入不断的交换之中,因此需要再加一个终止条件:上次交换的数和这次的相同,则没必要交换。
代码:
1 int firstMissingPositive(int A[], int n) { 2 // IMPORTANT: Please reset any member data you declared, as 3 // the same Solution instance will be reused for each test case. 4 int i, j; 5 int max = 0; 6 if(n == 0) 7 return 1; 8 for(i = 0; i < n; i++){ 9 if(A[i] > max && A[i] <= n){ 10 max = A[i]; 11 } 12 } 13 for(i = 0; i < n; i++){ 14 if(A[i] <= 0) 15 A[i] = max+1; 16 } 17 for(i = 0; i < n; i++){ 18 if(abs(A[i]) <= max && abs(A[i]) <= n){ 19 if(A[abs(A[i])-1] > 0) 20 A[abs(A[i])-1] = -A[abs(A[i])-1]; 21 } 22 } 23 for(i = 0; i < n; i++){ 24 if(A[i] > 0){ 25 return i+1; 26 } 27 } 28 return max+1; 29 }
第二次代码写得比参考文献里的要清楚些,其实只要把小于等于0的数处理为大于n的数即可。
1 int firstMissingPositive(int A[], int n) { 2 // IMPORTANT: Please reset any member data you declared, as 3 // the same Solution instance will be reused for each test case. 4 if(n == 0) 5 return 1; 6 int i; 7 for(i = 0; i < n; i++){ 8 if(A[i] <= 0) 9 A[i] = n+1; 10 } 11 for(i = 0; i < n; i++){ 12 if(abs(A[i]) <= n && A[abs(A[i])-1] > 0) 13 A[abs(A[i])-1] = -A[abs(A[i])-1]; 14 } 15 for(i = 1; i <= n; i++){ 16 if(A[i-1] > 0) 17 return i; 18 } 19 return i; 20 }