1. 题目大意
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
2. 思路分析
示例 1:
输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。
根据示例,对于一个长度为 N 的数组,其中没有出现的最小正整数只能在[1, N+1]
中。这是因为如果[1, N]
都出现了,那么答案是N+1
,否则答案是 [1, N]
中没有出现的最小正整数。
题目又要求我们不能使用额外空间,所以我们直接对原数组进行操作。因为我们只需要关注数组中[1, N]
之间的数,所以可以把值i
, 置换到数组对应下标的位置,得到nums[i-1] = i
。这里忽略<=0 & >N
的值。
上面提到过程中,本质上涉及元组两个元素的交换,如果nums[i-1] != i
则需要反复执行上面的流程。示例1变换流程:
array | index | 说明 |
---|---|---|
[3, 4, -1, 1] | 0 | |
[-1, 4, 3, 1] | 0 | 首先交换3和-1,虽然nums[0]!=1 但是出现负值接着往前走 |
[-1, 1, 3, 4] | 1 | 置换1和4 |
[1, -1, 3, 4] | 1 | 因为nums[1] != 2 , 所以继续置换,使得nums[0] = 1 |
[1, -1, 3, 4] | 2 | nums[2] = 3 ,继续 |
[1, -1, 3, 4] | 2 | nums[3] = 4 ,结束 |
最后我们只需要查找置换后数组中nums[i] != i-1
的情况,如果数组中都没有出现上述情况则直接返回N+1
。
3. 代码示例
Java版本
class Solution {
public int firstMissingPositive(int[] nums) {
for(int i=0; i<nums.length; i++){
while(nums[i] > 0 && nums[i] <= nums.length && nums[i] != i+1 &&
nums[i] != nums[nums[i]-1]){
// nums[i] != nums[nums[i]-1]:避免数组中有值重复的情况
int n = nums[i]-1;
int tmp = nums[n];
nums[n] = nums[i];
nums[i] = tmp;
}
}
System.out.println(Arrays.toString(nums));
for(int i=0; i<nums.length; i++){
if(nums[i] != (i+1) || nums[i] <= 0)
return i+1;
}
return nums.length+1;
}
}
Python版本
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
l = len(nums)
if l == 0:
return 1
for i in range(l):
while(1 <= nums[i] <= l and nums[i] != i+1 and nums[i] != nums[nums[i] - 1]):
# nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
for i in range(l):
if nums[i] != i+1:
return i+1
return l+1
在上述的while循环中nums[i] != nums[nums[i]-1]是为了避免数组中有值重复的情况,如果不加出处理就会一直被置换,陷入死循环。