题目
给你一个未排序的整数数组 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 没有出现。
分析
对于一个长度为 n 的数组,没有出现的最小正整数只能在 [1,n+1] 中。因为如果 [1,n] 都出现了,那么答案是 n+1,否则答案是 [1,n] 中没有出现的最小正整数。
我们可以利用数组本身的索引来标记哪些正整数已经出现过。
原地哈希表法
整体思路
第一步:处理无效数字
遍历数组 nums
,将所有小于等于 0 或者大于 n
的数置为 n + 1
,因为这些数不会影响最终结果。
第二步:标记出现过的正整数
再次遍历数组,对于每个绝对值小于等于 n
的数 num
,将 nums[num - 1]
置为负数,以此标记 num
已经出现过。
第三步:找出第一个正数所在的索引
最后遍历数组,找到第一个正数所在的索引 i
,则 i + 1
就是没有出现的最小正整数。
特殊情况处理
如果数组中所有位置都被标记为负数,说明 1 到 n
都出现过,返回 n + 1
。
时间复杂度:O(),
是数组的长度
空间复杂度:O(1)
class Solution {
public:
int firstMissingPositive(std::vector<int>& nums) {
int n = nums.size();
// 第一步:将所有小于等于 0 或者大于 n 的数置为 n + 1
for (int i = 0; i < n; ++i) {
if (nums[i] <= 0 || nums[i] > n) {
nums[i] = n + 1;
}
}
// 第二步:利用数组索引标记出现过的正整数
for (int i = 0; i < n; ++i) {
int num = std::abs(nums[i]);
if (num <= n) {
nums[num - 1] = -std::abs(nums[num - 1]);
}
}
// 第三步:找出第一个正数所在的索引
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
// 如果数组中所有位置都被标记为负数,说明 1 到 n 都出现过,返回 n + 1
return n + 1;
}
};