【原地哈希】缺失的第一个正数

114 阅读2分钟

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;
    }
}