来源于Leetcode的第41题, 难度H
完成这个任务其实不算复杂,只需要使用set去重,然后依次查找。只是题目限制了算法复杂度和使用的空间。我们希望原地实现,且复杂度是N
一个有意思的思路是原地进行hash,思想是每次进行替换,把某个数字放到该数字的索引位置,最后检查哪个数字位置缺失即可。
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
for (int i = 0;i<n;i++){
while(nums[i]>0 && nums[i]<=n && nums[nums[i]-1] != nums[i]){
swap(nums, nums[i]-1, i);
}
}
for(int i = 0;i<n;i++){
if (nums[i]<0 || nums[i]>n || nums[i]-1!=i){
return i+1;
}
}
return n+1;
}
private void swap(int[] nums, int x, int y){
int cur = nums[x];
nums[x] = nums[y];
nums[y] = cur;
}
}
另外一个思路是是借助数组的索引,实现原地的哈希表。对于数组中的数字,我们将该数组对应的索引的数字设置为负数,最后我们遍历整个数组,发现的第一个不是负数的数字的索引就是缺失的。
这种方法我们首先需要进行预处理数组,将数组中小于等于0和大于n的数字都设为1,并且在处理索引时考虑,当数字为n对应到0的索引。
最后考虑特殊情况,首先是考虑1是否在其中,其次是先从1开始检查数组,如都为负数,再检查0的位置返回N。否则的话返回n+1.
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
# 基本情况
if 1 not in nums:
return 1
for i in range(n):
if nums[i] <= 0 or nums[i] > n:
nums[i] = 1
for i in range(n):
a = abs(nums[i])
if a == n:
nums[0] = - abs(nums[0])
else:
nums[a] = - abs(nums[a])
print(nums)
for i in range(1, n):
if nums[i] > 0:
return i
# 这个判断的顺序很重要,按照顺序判断
if nums[0] > 0:
return n
return n + 1
还可以通用的采用维护连续的序列的方法
class Solution(object):
def firstMissingPositive(self, nums):
## 利用哈希表,维护左右区间
# 注意一定要去重
nums = list(set(nums))
left = {}
right = {}
n = len(nums)
for i in nums:
if i > 0:
if i-1 in left and i+1 in right:
left[right[i+1]] = left[i-1]
right[left[i-1]] = right[i+1]
elif i-1 in left:
left[i] = left[i-1]
right[left[i-1]] = i
elif i+1 in right:
right[i] = right[i+1]
left[right[i+1]] = i
else:
left[i] = i
right[i] = i
if 1 in right:
return right[1]+1
else:
return 1