【题目】 给定一个无序整型数组arr,找到数组中未出现的最小正整数。
例如:
arr = [-1, 2, 3, 4],返回1
arr = [1, 2, 3, 4],返回5
【基本思路】
整个过程可以做到时间复杂度O(N),空间复杂度O(1)。
生成变量left和right,left表示遍历到目前为止,数组中已经包含的正整数的范围是[1…left],初始时令left = 0表示没有arr目前没有包含任何正整数;right表示遍历到目前为止,在后续情况出现最优的情况下,arr可能包含的正整数的范围是[1, right],初始时令right = N,表示arr可能包含1~N的所有正整数。 arr[left]正确存储的值是left+1。
利用left从左到右遍历数组:
如果arr[left] = left + 1。在没有遍历arr[left]之前,arr已经包含的正整数的范围是[1,left],此时出现了arr[left] = left + 1,说明arr包含的正整数的范围可以扩到[1, left+1],即令left + 1.
如果arr[left] <= left。在没有遍历arr[left]之前,arr中的正整数的范围已经是[1, left],所以需要的是[left+1, right]上的数,而此时arr[left] <= left,说明[left+1, right]上少了一个数,所以arr在后续最优的情况下,可能包含的正整数的范围缩小到[1, right-1],此时把arr最后位置的数arr[right-1]放在位置left上,下一步检查这个数,然后令right - 1.
如果arr[left] > right,与步骤2同理,把arr最后位置的数arr[right-1]放在位置left上,下一步检查这个数,然后令right - 1.
如果arr[arr[left]-1] = arr[left]。如果步骤2、3没中,说明arr[left]是在[left+1, right]范围上的,而且这个数应该放置在arr[left] - 1位置上,但是发现此时arr[left] - 1位置上的数已经为arr[left],说明这个数出现了两次,既然在[left+1, right]上出现了重复值,那么[left+1, right]范围上的数又少了一个,所以把arr最后位置的数arr[right-1]放在位置left上,下一步检查这个数,然后令right - 1.
- 如果步骤2、3、4都没中,说明发现了[left+1, right]上的数,并且未发生重复。那么arr[left]应该放在位置arr[left] - 1上,所以把left位置上的值和arr[left] - 1位置上的值交换,下一步继续遍历位置left上的数。
C++源码:int missNum(int arr[], int length)
{
int left = 0;
int right = length;
while(left < right)
{
if(arr[left] == left + 1)
{
left++;
}
else if(arr[left] < left+1 || arr[left] > right || arr[arr[left]-1] == arr[left])//不合法
{
arr[left] = arr[right - 1];
right--;
}
else
{
swap(arr[left], arr[arr[left]-1]);
}
}
return left+1;
}
来源