Leetcode上的41. 缺失的第一个正数,难度:困难。
题目描述
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例1:
输入:nums = [1,2,0]
输出:3
示例2:
输入:nums = [3,4,-1,1]
输出:2
示例3:
输入:nums = [7,8,9,11,12]
输出:1
算法思想
我们只要从正数1开始遍历就好了,遍历的长度是数组的长度就可以。在遍历的过程中检查对应元素是否存在,如果我们不考虑它的要求的话,使用哈希表就可以了。但这样是不符合空间复杂度的。如果使用二分,先将nums进行排序,在排好序的基础上使用二分查找,查找从[1,len]之间的元素,如果不存在,就满足了我们的要求。时间复杂度为O(NlogN),不符合要求。
因此我们可以在nums的基础上实现一个哈希表,即原地哈希。就是遍历数组中每一个元素,让每一个元素放到它正确的位置,前提是nums[i]要小于len,这样才能有正确的位置。当nums[i] < 1或者nums[i] == nums[nums[i] - 1],直接向下继续遍历就可以。
注意:这三种方法最后返回都需要返回len+1。就是上面没有不符合要求的i,那么数组中的元素都是符合要求的,此时需要返回数组最后一个元素+1;
方法一:哈希算法
public class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
Set<Integer> hashSet = new HashSet<>();
for (int num : nums) {
hashSet.add(num);
}
for (int i = 1; i <= len ; i++) {
if (!hashSet.contains(i)){
return i;
}
}
return len + 1;
}
}
方法二:二分查找
class Solution {
public int firstMissingPositive(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
for(int i = 1; i <= n; i++) {
int res = search(nums, i);
if(res == -1) return i;
}
return n + 1;
}
int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target) return 1;
else if(nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
方法三:原地哈希
class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; i++) {
while(nums[i] >= 1 && nums[i] <= len && nums[i] != nums[nums[i] - 1]) {
swap(nums, i, nums[i] - 1);
}
}
for(int i = 0; i < len; i++) {
if(nums[i] != i + 1) {
return i + 1;
}
}
return len + 1;
}
void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}