缺失的数字分析
原题
给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。
分析
首先数组是无序的,找到第一个大于0且不在数组中的元素,就是要找到大于0且不在数组中的最小的整数。要怎么处理呢?要找到最小的,不妨尝试从小到 大排序,然后从1开始,查找是否在数组中,可以利用二分查找。这样整体的时间复杂度是O(nlogn),空间复杂度是O(1)。
离题目的要求,还差一些。如何改进呢?针对排序好的数组,我们做如下的观察:
-
当缺失的数字为1的时候,A[0]<=0, A[1] != 1
-
当缺失的数字为2的时候,A[0]<=0, A[1] = 1, A[2] != 2
-
当缺失的数字为3的时候,A[0]<=0, A[1] = 1, A[2] = 2, A[3] != 3
通过上面的观察,可以发现,其实并不需要整个数组是有序的,只需要让数组中的0<A[i]<n的,能够还原到A[i]的位 置,就像缺失3的时候,使得A[1] = 1, A[2] = 2,而且,这两个一定是这样的。后面的序就无需理会了。能够做到,不排序,就实现位置的还原么?且看如下的算法:
由后到前扫描数组:
-
如果A[i]=n,则continue;
-
如果A[i]=A[A[i]],则continue;
-
如果0<A[i]<n,那么可以还原,则swap(A[i], A[A[i]]),然后跳转到步骤2
然后,数组中如果存在0<A[i]<n的,已经保证A[i]=i了。此时:
-
从1开始遍历,如果A[i]!=i,则i就是要找的数
-
如果遍历完,没有找到,则说明i从[1,n-1],都有A[i]=i,那么这个时候就看A[0],如果A[0]是n,则返回n+1,如果A[0]不是n,则返回n,即可。
上面算法的时间复杂度是O(n)么?有的同学会想,在swap之后,仍然到第2步,可能还会出现交换,所以时间复杂度要高于O(n)。但实际上,swap之后,是会发生再次的交换,但我们可以保证,每一次交换,都会使得一个数字还原,即A[i]=i,再一次交换,会让一个新的数字还原。 假设数组中,可以还原的数字个数为k,则需要交换k次,k最多为n-1。这里要很小心,因为总的交换次数是k,并不是对于每一个元素,都会产生k次交换。 如果是后者,总的时间复杂度就是O(kn)了,但因为总的交换次数是k,所以总的时间复杂度是O(n+k),就是O(n)。
【分析完毕】