题目描述
给你一个未排序的整数数组 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-231 <= nums[i] <= 231 - 1
题目思路 Self
- 使用一个 map 记录所有 num 存在情况
- 从 1 开始判断 map 中是否存在,递增以找到第一个未出现的正数
func firstMissingPositive(nums []int) int {
minPositiveNum := 1
exists := make(map[int]bool)
for _, num := range nums {
exists[num] = true
}
for exists[minPositiveNum] {
minPositiveNum++
}
return minPositiveNum
}
Claude 思路
- 对于一个长度为 n 的数组,缺失的最小正整数一定在 [1, n+1] 范围内。
- 我们可以将数组本身作为哈希表来标记已经出现的正数。
- 具体做法是:对于数组中的每个元素 x,如果 1 ≤ x ≤ n,则将 nums[x-1] 标记为负数。
- 最后遍历数组,第一个正数的位置 i+1 就是缺失的最小正整数。
代码实现
func firstMissingPositive(nums []int) int {
n := len(nums)
// 第一步:将数组中小于 0 的数修改为 n+1
// 因为我们只关心 1 到 n 之间的正整数
// 数组大小为 n,因此缺失的正整数只能位于 [1, n+1] 之间
// [2, 3, ..., n] 缺失 1
// [1, 2, 3, ..., n] 缺失 n+1
for i := 0; i < n; i++ {
if nums[i] <= 0 {
nums[i] = n+1
}
}
// 第二部:将出现的正数位置对应位置的数标记为负数
// 使用数组索引模拟哈希表,num 对应的数组索引为 num-1
for i := 0; i < n; i++ {
num := abs(nums[i])
if num <= n {
// 将对应位置的值变为负数,表示这个位置的数已经出现
// 需要使用绝对值防止重复标记导致正负抵消
nums[num-1] = -abs(nums[num-1])
}
}
// 第三步:找到第一个正数的位置
for i := 0; i < n; i++ {
if nums[i] > 0 {
// 位置 i 对应的数是 i+1,所以第一个正数的位置对应的值 i+1 就是缺失的第一个正整数
return i + 1
}
}
// 如果数组中包含了 1 到 n 的所有正整数,那么缺失的最小正整数就是n+1
return n+1
}
func abs(a int) int {
if a < 0 {
return -a
}
return a
}
关键数据变化表
以示例2 nums = [3,4,-1,1] 为例,展示算法执行过程中的数据变化:
第一步:将数组中<=0的数替换为n+1(这里n=4)
| 索引 | 原始值 | 处理后 | 说明 |
|---|---|---|---|
| 0 | 3 | 3 | 无需修改 |
| 1 | 4 | 4 | 无需修改 |
| 2 | -1 | 5 | 替换为n+1=5 |
| 3 | 1 | 1 | 无需修改 |
第一步处理后,数组变为:[3,4,5,1]
第二步:标记已存在的正整数
| 索引 | 当前值 | 需标记的位置 | 处理后 | 说明 |
|---|---|---|---|---|
| 0 | 3 | nums[2] | [3,4,-5,1] | 标记位置3-1=2的元素为负 |
| 1 | 4 | nums[3] | [3,4,-5,-1] | 标记位置4-1=3的元素为负 |
| 2 | -5 | - | [3,4,-5,-1] | 已经是负数,表示绝对值为5的数存在,但5>n,超出范围,忽略 |
| 3 | -1 | nums[0] | [-3,4,-5,-1] | 标记位置1-1=0的元素为负 |
第二步处理后,数组变为:[-3,4,-5,-1]
第三步:寻找第一个正数的位置
| 索引 | 值 | 是否为正数 | 说明 |
|---|---|---|---|
| 0 | -3 | 否 | 表示位置0+1=1存在于原数组 |
| 1 | 4 | 是 | 表示位置1+1=2不存在于原数组 |
| 2 | -5 | 否 | 表示位置2+1=3存在于原数组 |
| 3 | -1 | 否 | 表示位置3+1=4存在于原数组 |
在索引1处找到了正数,表示位置1+1=2的数不存在于原数组,因此缺失的最小正整数是2。
考察知识点
这道题主要考察以下知识点:
-
原地哈希算法:利用数组本身作为哈希表,避免使用额外空间。
-
索引与值的映射关系:使用数组索引和值之间的关系来存储信息。
-
时间和空间复杂度优化:在 O(n) 时间和 O(1) 空间复杂度下解决问题。
-
数组元素标记技巧:使用正负号作为标记手段,而不改变元素的绝对值。
-
问题转化能力:将"找缺失的最小正整数"转化为"找第一个未被标记的位置"。
-
边界条件处理:
- 处理非正数
- 处理超出数组长度范围的数
- 处理重复元素
-
代码简洁性:在保持算法复杂度的前提下,使用清晰简洁的代码实现复杂的逻辑。
-
分析能力:理解缺失的最小正整数一定在 [1, n+1] 范围内。
这道题是一个典型的"原地哈希"问题,考察了如何在有限的空间内高效地解决问题。这种技巧在处理需要标记元素出现情况而又不允许使用额外空间的场景中非常有用。