【题目】
给定一个无序整型数组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的所有正整数。
利用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[r-1]放在位置left上,下一步检查这个数,然后令right - 1.
如果arr[left] > right,与步骤2同理,把arr最后位置的数arr[r-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[r-1]放在位置left上,下一步检查这个数,然后令right - 1.
- 如果步骤2、3、4都没中,说明发现了[left+1, right]上的数,并且未发生重复。那么arr[left]应该放在位置arr[left] - 1上,所以把left位置上的值和arr[left] - 1位置上的值交换,下一步继续遍历位置left上的数。
下面是使用python3.5实现的代码。
def missNum(arr):
if arr == None or len(arr) == 0:
return
left = 0
right = len(arr)
while left < right:
if arr[left] == left + 1:
left += 1
elif arr[left] <= left or arr[left] > right or arr[arr[left]-1] == arr[left]:
arr[left] = arr[right-1]
right -= 1
else:
tmp = arr[left]
arr[left] = arr[arr[left]-1]
arr[tmp-1] = tmp
return left + 1