思路
对于长度为 n 的数组,缺失的最小正整数必然在 [1, n+1] 范围内。如果 1 到 n 全都出现,答案就是 n+1;否则就是其中缺失的那个。
题目要求 O(n) 时间和 O(1) 空间,没办法用额外的哈希表。但我们可以把数组本身当成'哈希表'——让索引 i 对应数字 i+1。只要能标记某个数字是否出现过,最后扫一遍数组就能找到答案。
有两种做法:
- 原地置换:把每个在 [1, n] 内的数字 x 交换到下标 x-1 的位置。最后哪个位置上的数不对,对应的 i+1 就是缺失的。
- 正负号标记(官方推荐):用数组元素的符号表示该索引对应的数字是否出现过。负数表示出现过,正数表示没出现。
两种方法都满足要求,下面分别给出实现。
解法一:原地置换
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
// 把 1~n 的数放到它们该去的位置
while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
int temp = nums[nums[i] - 1];
nums[nums[i] - 1] = nums[i];
nums[i] = temp;
}
}
for (int i = 0; i < n; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
return n + 1;
}
循环里的交换条件 nums[nums[i] - 1] != nums[i] 是为了避免死循环。当目标位置已经放了正确的数时,没必要再交换。
以 [3,4,-1,1] 为例,置换后的结果是 [1, -1, 3, 4]。扫描发现索引 1 的位置是 -1 而不是 2,返回 2。再看 [1,2,0],置换后是 [1,2,0],索引 2 的 0 不等于 3,返回 3。 中所有数都超出 [1,5] 范围,不动,直接返回 1。


