题目
题目描述
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。
示例 2:
输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。
示例 3:
输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。
提示:
1 <= nums.length <= 105
-2^31 <= nums[i] <= 2^31 - 1
题解
这个问题可以通过一种巧妙的方法在 O(n) 时间复杂度和常数级别的额外空间内解决。核心思想是利用数组的索引作为哈希键,将每个正整数放置到它应该在的位置上。具体来说,就是让数值 1
放置在索引 0
处,数值 2
放置在索引 1
处,依此类推。
解决思路
-
遍历数组:首先遍历数组,将每个位于范围
[1, n]
内的数字放到它对应的索引位置上(即数值x
应该放在索引x-1
)。如果一个数字已经在正确的位置上或者不在有效范围内(小于等于0
或大于n
),则跳过这个数字。 -
第二次遍历:再次遍历数组,找到第一个不匹配其索引 + 1 的位置,这个位置的索引 + 1 就是缺失的第一个正整数。如果所有位置都匹配,则说明数组包含了从
1
到n
的所有数字,因此答案是n+1
。
Python 实现代码
以下是按照上述思路实现的 Python 代码:
def firstMissingPositive(nums):
if not nums:
return 1
n = len(nums)
# 第一步:把不在 [1, n] 范围内的数替换为 n+1(或任何大于 n 的数)
for i in range(n):
if nums[i] <= 0 or nums[i] > n:
nums[i] = n + 1
# 第二步:使用索引和数值之间的关系来标记哪些数字出现过
for i in range(n):
num = abs(nums[i])
if 1 <= num <= n:
nums[num - 1] = -abs(nums[num - 1])
# 第三步:找到第一个正数的索引,这就是缺失的第一个正整数
for i in range(n):
if nums[i] > 0:
return i + 1
# 如果所有索引处都是负数,则意味着 [1, n] 都存在,所以返回 n+1
return n + 1
代码解释
-
第一步:将所有不在
[1, n]
范围内的数字替换成n+1
,因为我们只关心[1, n]
范围内的数字。 -
第二步:遍历数组,对于每一个在
[1, n]
范围内的数字num
,我们将它对应的索引num-1
处的值变为负数,以此来标记这个数字已经出现过。注意这里我们使用了绝对值函数abs()
来确保即使某个位置已经被标记为负数,我们仍然可以正确地访问原始值。 -
第三步:最后再遍历一次数组,寻找第一个仍然是正数的位置,这表示对应的正整数没有出现在数组中。如果整个数组都被标记成了负数,则说明
[1, n]
中的所有数字都在数组中出现了,因此返回n+1
。
这种方法不仅满足了时间复杂度 O(n) 和常数级别额外空间的要求,而且非常直观易懂。