【LeetCode】缺失的第一个正数 [H](双指针)

41. 缺失的第一个正数 - 力扣(LeetCode)

一、题目

给你一个未排序的整数数组 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.length <= 5 * 105
  • -231 <= nums[i] <= 231 - 1

二、代码

class Solution {
    public int firstMissingPositive(int[] nums) {
        // 过滤无效参数
        if (nums == null || nums.length == 0) {
            return 1;
        }

        // l左边都是有效区的数。有效区的所有书都满足nums[i] = i + 1
        int l = 0;
        // r及其r右边的数都是垃圾区。永远不可能加入到有效区的数,就加入垃圾区
        int r = nums.length;
        while (l != r) {
            // 1、如果l位置的数等于l+1,说明这个数是有效区的数,加入有效区
            if (nums[l] == l + 1) {
                // 有效区右扩
                l++;
            // 2、如果l位置的数已经存在在有效区了(nums[l] <= l)  或者  l位置的数大于r   或者在nums[l] - 1已经存在符合有效区规则的nums[l]了,就将l位置的数加入到垃圾区
            } else if (nums[l] <= l || nums[l] > r || nums[nums[l] - 1] == nums[l]) {
                // 将l交换到垃圾区,并且将垃圾区左扩
                swap(nums, l, --r);
            // 如果nums[nums[l] - 1] != nums[l],就将l和nums[l] - 1位置的数交换
            } else {
                swap(nums, l, nums[l] - 1);
            }
        }

        // r+1就是缺少的最小的正数
        return r + 1;
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

三、解题思路 

假设L来到下标17位置,说明0~16位置属于有效区,有效区内已经存好了1....17依次增加的正整数,那么此时L位置的数,什么情况下才是要被放到垃圾区呢?

1) L位置的数 <= L,说明这个数已经存在于有效区了,要将其放到垃圾区,已经收集过的数字会让最好情况变差,因为会占用空间。

2)如果L位置的数 > R,那么它也是垃圾区的数,因为大小超过R,前面一定有不存在的更小的正整数。很好理解,假设有5个位置,然后放1~10大小的数,其中的数肯定有断层的时候,这个断层中就有最小的没出现过的正整数。

3)假设此时R在31位置,L在17位置。如果L位置上的数是23,我们不能确定他是不是垃圾,因为它小于等于R,后面可能成为有效区的数。这个时候23如果是有效区的数,那么他就应该放在下标22位置,所以将他和下标22位置的数交换:

  1. 如果此时发现下标22位置的数已经有23了,那么L位置的23就是重复了,直接和R-1位置交换,R--,将其加入垃圾区。
  2. 如果下标22位置的数不是23,就L位置和下标22位置的数交换,然后再用相同的流程去判断此时L交换过来的数符合那种情况。

4)如果此时L位置的数等于L+1,那么这个数就是有效区的,将L右移,将其加入有效区。

你可能感兴趣的:(数据结构,算法,LeetCode,leetcode,算法,双指针,Java,快速排序)