🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
题目解析
这道题要求在一个未排序的整数数组中,找出没有出现的最小正整数,并且需要满足两个严格的条件:
- 时间复杂度:O(n)
- 空间复杂度:O(1)(常数级额外空间)
直接排序或使用哈希表的方法,要么时间复杂度不达标,要么空间复杂度不达标。因此,我们需要采用一种巧妙的原地置换策略,利用数组本身作为哈希表来解决问题。
核心思路
对于一个长度为 n 的数组,缺失的最小正整数只可能在 [1, n+1] 范围内:
- 如果
[1, n]内的所有整数都出现了,那么缺失的就是n+1。 - 否则,缺失的就是
[1, n]中第一个未出现的数。
我们的目标就是把数组中所有在 [1, n] 范围内的数,放到它们 "应该" 在的位置上,即数值 k 应该放在索引 k-1 的位置上。这样,我们只需要遍历一次数组,找到第一个位置 i 上的数不等于 i+1,i+1 就是我们要找的答案。
详细步骤
- 原地置换 :遍历数组,对于每个元素
nums[i],如果它是一个在[1, n]范围内的正整数,并且它没有在正确的位置nums[i]-1上,就将它与nums[nums[i]-1]交换,直到当前位置的元素正确或超出范围为止。 - 查找缺失 :再次遍历数组,检查每个位置
i。如果nums[i] != i+1,说明i+1就是缺失的最小正整数。 - 全包含 :如果遍历结束后所有位置都正确,说明
[1, n]都存在,返回n+1。
完整代码
cpp
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int n = nums.size();
// 第一步:原地置换,将[1, n]范围内的数放到正确位置
for (int i = 0; i < n; ++i) {
// 当nums[i]在[1, n]范围内,且不在正确位置时,进行交换
while (1 <= nums[i] && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {
int j = nums[i] - 1; // 计算正确的位置索引
swap(nums[i], nums[j]); // 交换到正确位置
}
}
// 第二步:遍历查找第一个缺失的正整数
for (int i = 0; i < n; ++i) {
if (nums[i] != i + 1) {
return i + 1;
}
}
// 如果[1, n]都存在,返回n+1
return n + 1;
}
};
复杂度分析
- 时间复杂度 :O(n)
- 虽然有两层循环,但每个元素最多被交换到其正确的位置一次,因此总的交换次数是 O (n)。两次线性遍历也是 O (n),所以整体时间复杂度为 O (n)。
- 空间复杂度 :O(1)
- 我们只在原数组上进行操作,没有使用额外的辅助空间,满足了常数级空间复杂度的要求。
示例演示
以 nums = [3, 4, -1, 1] 为例:
- 初始数组:
[3, 4, -1, 1] - 处理
i=0,nums[0]=3,应在位置2,交换后数组变为[-1, 4, 3, 1]。 - 处理
i=1,nums[1]=4,应在位置3,交换后数组变为[-1, 1, 3, 4]。 - 处理
i=1,nums[1]=1,应在位置0,交换后数组变为[1, -1, 3, 4]。 - 处理
i=2,nums[2]=3,已在正确位置;i=3,nums[3]=4,已在正确位置。 - 最终数组:
[1, -1, 3, 4]。 - 遍历查找,发现
i=1时nums[1] != 2,所以返回2。