leetcode41. 缺失的第一个正数(双指针解法,看完还不会你打我)

112 阅读4分钟

题目链接:41. 缺失的第一个正数 - 力扣(LeetCode)

双指针

设n为nums数组的个数

首先,根据这道题目我们可以先假设一个理想状况,那就是nums数组中的每一个i位置上都放着i + 1的数值(0 <= i <= n - 1),那么这个理想数组缺失的第一个正整数显而易见,就会是n + 1

举个例子,假如n为4的话,这个理想的nums数组就会是 [1,2,3,4],那么毫无疑问,缺失的第一个正整数就会是5(n = 4,n + 1 = 5);

假如n为8的话,这个理想数组就会是 [1,2,3,4,5,6,7,8] ,也毫无疑问,缺失的第一个正整数就会是9(n = 8,n + 1 = 9);

如果题目给定的所有数组都是理想数组就好了,这样我们就可以很轻松地求出答案,理想很丰满,现实却是骨感的,并不是所有给定数组都是理想数组,那么我们就需要在现实数组里面构造出一个子理想数组(连续的)求出答案。

那么接下来,我们整个题目求解的思路就是在给定的nums数组里面构造出这样的理想数组。

解题步骤

第一步:

让我们给定两个指针l和r,让l初始值为0,r 为 n(也就是数组下标的第一个越界位置)。这样我们的想法就是让nums数组中取区间 [l ,r]的子数组尽量变成理想数组,为了能做到这样,在l和r相向遍历(双向奔赴)的过程中,我们给出如下定义:

1.[0,l)(左闭右开区间)区间的子数组已经为理想数组。

2.[r,n - 1]区间的子数组不是理想数组(我们起另外一个名字,就叫它垃圾区吧),刚开始的时候r > n - 1,也就是说这个子数组是空的,没有元素。

3.剩下中间的子数组就是l和r两个指针待遍历的区域。

第二步:

如何在遍历的过程中,让[0,l)区间的子数组变为理想数组呢?

1.首先,如果nums[l] == l + 1的话,那么就符合理想数组的要求,我们让l指向下一个坐标就好,也就是进行l ++的操作;

2.第二,如果nums[l] != l + 1的话,我们分为两种情况考虑:

我们知道l到r的区间如果要让它变成理想数组,那么这个区间数组的数值范围必须是 [l + 1,r] ,并且数值之间不能重复

(1)第一种情况,如果nums[l]没有命中这个区间,说明nums[l]没有机会成为这个理想数组的一员,就把它发配到垃圾区中去,怎么发配呢?扩充垃圾区(r --),交换nums[l]与nums[r]的值( swap(nums[l],nums[r])  )。

(2)第二种情况,nums[l]命中了这个区间,那么它可以成为理想数组的一员,那么它应该去的位置就应该是nums[l] - 1,但如果这个位置上的数值( nums[nums[l] - 1]  )与 nums[l] 是一样的,那不好意思,你违背了理想数组的不能重复规则,也把你发配到垃圾区中去,发配规则与第一种情况一样。如果不一样(nums[nums[l] - 1] != nums[l] ),那这个数值就来到这个理想数组中应有的位置,与原先占着这个位置的数值进行交换( swap(nums[l], nums[nums[l] - 1])  )。

最后:

当l和r相遇的时候(l = r),至此, [0,l) 区间的数组已然成为理想数组,而 [r,n - 1] 的垃圾区中的所有数字都是不符合理想数组的条件的,那么答案就会是我们这个构建出来的理想数组的大小再加一(此时l就是数组的大小,答案就会是l + 1),那么为什么l + 1就是答案呢?

证明

假设l + 1不是答案的话,那么经过两指针的遍历,它就必然会在理想数组里面(换一句话说就是它不是缺失的第一个正整数),而数组的大小是l,[0,l)为理想数组,数值范围在[1,l + 1)(左闭右开区间)上,可见l + 1并不在上面,产生了矛盾性,从而证明了l + 1就是答案。

代码

思路看起来可能会复杂一些,但是我们的代码实现会非常简单,代码量极其简短。

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        int l = 0, r = n;
        while (l != r) {
            if (nums[l] == l + 1) {
                l++;
            } else {
                if (l + 1 <= nums[l] && nums[l] <= r &&
                    nums[nums[l] - 1] != nums[l]) {
                    swap(nums[l], nums[nums[l] - 1]);
                } else {
                    r--;
                    swap(nums[l], nums[r]);
                }
            }
        }
        return l + 1;
    }
};

可以去力扣看看我的其他题解,有帮助到的双击一下屏幕吧~

笔者力扣个人主页