解法:原地哈希
将数组中的数字 x x x,映射到数组中的位置 x − 1 x-1 x−1处。
考虑极端情况:
(1)数组长度为
N
N
N,是从
1
1
1开始的正整数序列,如
1
,
2
,
3
,
.
.
.
,
N
,
1,2,3,...,N,
1,2,3,...,N,则没出现的那个最小的正整数为
N
+
1
N+1
N+1。
(2)数组长度为
N
N
N,不含
[
1
,
N
]
[1,N]
[1,N]范围的任何数字,则没有出现的最小正整数为
1
1
1。
除了(1)这种极端情况,没有出现的最小正整数必然在 [ 1 , N ] [1,N] [1,N]范围。
对于长度为 N N N的数组,映射完成后,在 [ 1 , N ] [1,N] [1,N]范围内的数字都映射到了合适的位置,而且是按从
小到大的顺序排列的。而其他数字(该范围内重复的数字,不在此范围的数字)占据了剩下的位置。
这时,只需要从数组序号 0 0 0的位置开始枚举,找到第一个不符合该哈希映射的数字所在位置,就
找到了没有出现过的那个最小的正数。
举个例子进行理解,来自评论区某有才老哥:
c语言版本代码:
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int firstMissingPositive(int* nums, int numsSize){
for(int i = 0; i<numsSize; i++)
{
// 映射规则,nums[i] --> nums[nums[i]-1]
// nums[i]-1 作为数组下标应满足nums[i]-1>=0,即nums[i]>0
// nums[i]-1 作为数组下标应满足nums[i]-1<numsSize,即nums[i]<=numsSize
// 如果数组中有重复数,可能会出现nums[i] == nums[nums[i]-1]的情况,
// 这时表明该数已放在正确的地方,及时跳出循环, 否则会陷入无限死循环
while(nums[i] > 0 && nums[i] <= numsSize && nums[i] != nums[nums[i]-1] )
{
swap(&nums[i], &nums[nums[i]-1]);
}
}
// 映射完后,数组i处的值应是i+1,若不是,则是没有出现过的那个最小的正数
for(int i=0; i<numsSize; i++)
{
if(nums[i] != i+1)
{
return i+1;
}
}
//如果数组是从1开始的正数序列,应该返回 numsSize+1
//:[1,2,3],应该返回4.
return numsSize + 1;
}