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(1)的空间,可以之间开一个O(n)的数组,全部标记为0,然后A中出现过的标记为1,即可解决此问题。
但是此题要求O(1)的空间,我们用A[i]保存i+1,这样最好做一次判断A[i] 不是i+1的内容即可达到目的。
虽然不能再另外开辟非常数级的额外空间,但是可以在输入数组上就地进行swap操作。
1 class Solution { 2 public: 3 int firstMissingPositive(int A[], int n) { 4 for(int i = 0 ; i < n; ) { 5 if((A[i] > 0) && (A[i] <= n) && (A[A[i]-1] != A[i])) { 6 int t = A[A[i]-1]; 7 A[A[i]-1] = A[i]; 8 A[i] = t; 9 } else i++; 10 } 11 for(int i = 0 ; i < n; i++) { 12 if(A[i] != i+1) return i+1; 13 } 14 return n+1; 15 } 16 };
下图以题目中给出的第二个例子为例,讲解操作过程。
首先思路上,其次临界条件,这题和下面题异曲同工:
n个元素的数组,里面的数都是0~n-1范围内的,求数组中重复的某一个元素,没有返回-1, 要求时间性能O(n) 空间性能O(1)。
1 bool IsDuplicateNumber(int *array, int n) 2 { 3 if(array==NULL) return false; 4 int i,temp; 5 for(i=0;i<n;i++) 6 { 7 while(array[i]!=i) 8 { 9 if(array[array[i]]==array[i]) 10 return true; 11 temp=array[array[i]]; 12 array[array[i]]=array[i]; 13 array[i]=temp; 14 } 15 } 16 return false; 17 }
把每个数放到自己对应序号的位置上,如果其他位置上有和自己对应序号相同的数,那么即为有重复的数值。
当没有重复数值出现时,可以看做一种特殊的排序。
看到“里面的数都属于范围[0,n-1]”会不会条件反射想到计数排序? 可惜题目要求O(1)空间,而且仔细看题就知道与计数排序八竿子打不着。不过计数排序里面蕴含的思想我们还是可以使用的。
回忆一下计数排序的算法,如果待排序的值大小范围在[0,n-1]之内,我们完全可以构造一个数组B[n],将待排序数组中,值为x(0<=x<=n-1)的元素放到B[x]中去,这里暂时先不说重复的情况。说白了就是如果有元素的值范围都在一个固定区间内的情况,那么我们完全可以把元素的值与数组下标映射起来。那么这道题也一样。
下面给出一位网友的做法,就原数组的值和下标进行交换,然后新值是个标记。o(n)内可以找到相同标记就返回。只要1个中间变量。当然这个做法可不一定正确,也不一定是最优的。大家仁者见仁智者见智哈。