给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
问题背景
在给定一个整数数组 nums
后,我们的任务是找出数组中 缺失的最小正整数。这道题的难点在于:必须 原地操作,即不使用额外的空间,并且 时间复杂度 需要保持在 O(n)
。
问题的核心是:如何在给定的数组中,找到不在 [1, n]
范围内的最小正整数,其中 n
是数组的大小。
解法思路
最直观的解法是通过 排序 数组,然后遍历查找缺失的最小正整数。然而,排序的时间复杂度是 O(n log n)
,而我们要求的是线性时间复杂度 O(n)
,因此排序不是最优解。
一种更加高效的解决方法是 通过原地交换将每个数字放到它该在的位置,然后遍历数组,寻找第一个缺失的正整数。我们利用数组本身的索引来为每个数字找到对应的位置,从而降低空间复杂度。
具体步骤
时间和空间复杂度
-
把每个数字放到它应该去的位置:
- 假设数字
x
应该放在索引x - 1
位置上。通过交换,保证每个数字都尽量放在它应该在的位置。 - 交换的条件是:
nums[i]
在1
到n
之间,并且nums[i]
不在它应该在的位置上。
- 假设数字
-
遍历数组找缺失的最小正整数:
- 在交换完成后,遍历数组检查第一个不满足
nums[i] == i + 1
的位置。这个位置的索引对应的数字就是缺失的最小正整数。
- 在交换完成后,遍历数组检查第一个不满足
-
如果数组包含所有
1
到n
的正整数,则缺失的最小正整数是n + 1
。class Solution { public: int firstMissingPositive(vector<int>& nums) {//将数放在属于他的位置然后遍历一遍 int n=nums.size(); for(int i=0;i<nums.size();i++){ while(0<nums[i]&&nums[i]<=n&&nums[i]!=nums[nums[i]-1]) swap(nums[i],nums[nums[i]-1]);//确保这个数是在1-n内的有效字符 } for(int i=0;i<n;i++){ if(i+1!=nums[i]) return i+1; } return n+1; } };
代码解析
-
原地交换:
- 遍历数组,对于每个有效数字
nums[i]
,将它放到nums[nums[i] - 1]
的位置上。这样,所有有效数字都会被放置到数组的正确位置。 - 我们使用
while
循环来不断交换,直到当前数字在正确的位置上。
- 遍历数组,对于每个有效数字
-
查找缺失的最小正整数:
- 在完成交换后,遍历数组,找到第一个位置
i
,使得nums[i] != i + 1
。这个位置对应的数字i + 1
就是缺失的最小正整数。
- 在完成交换后,遍历数组,找到第一个位置
-
时间复杂度:
O(n)
,我们遍历数组进行交换操作,然后再遍历一次数组找缺失的正整数。每个元素最多交换一次,因此时间复杂度是线性的。 -
空间复杂度:
O(1)
,我们只用了常数空间来进行交换操作,没有使用额外的数据结构。-
返回结果:
- 如果数组中已经包含了所有的数字
1
到n
,那么缺失的最小正整数就是n + 1
。总结
通过原地交换,将数字放到正确的位置,最终我们可以找到缺失的最小正整数。这种方法的时间复杂度是
O(n)
,空间复杂度是O(1)
,非常高效。希望这个解法能够帮助你更好地理解和解决这类问题。
- 如果数组中已经包含了所有的数字
-