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

本文介绍了如何使用双指针方法解决LeetCode题目41,寻找在给定数组中缺失的第一个正整数。通过构造理想数组并不断调整实际数组,确保满足理想状态,最终找到答案。
摘要由CSDN通过智能技术生成

题目链接: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)区间的子数组变为理想数组呢?

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

二.第二,如果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就是答案。

代码

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

C++

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

  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值