LeetCode 41 缺失的第一个正数

先看题目。

这题是hard难度,实际上就是奇技淫巧。没做过这道题,你想破头都想不出来时间复杂度O(n)且空间复杂度O(1)的做法。

思路如下:

对于一个长度为n的nums的数组,缺失的第一个正数绝对是在[1,n+1]之间产生。如果从1到n都在数组中出现了,答案就是n+1。否则就是[1,n]的第一个没出现的数字。所以说,我们现在需要统计1到n是否在数组出现。我们可以用hashset去做这个事,遍历这个数组,将处于[1,n]之间的数字放到hashset中。遍历结束后,从1开始查,看哪个数字没出现,就是第一个缺失的正整数。

然而,这题要求是O(1)的额外空间,我们无法另外创建一个hashset。我们只能另辟蹊径,把这个数组本身当作一个hashset去用。我们的目的是要用这个数组来判断[1,n]是否出现过,而nums数组长度是n,那我们可以用nums[0]判断1是否出现过,nums[1]判断2是否出现过,以此类推,nums[n-1]判断n是否出现过。

那我们如何通过nums[n-1]去判断n是否出现过呢,这又是另外一个奇技淫巧——如果n出现过,我们把nums[n-1]改成负数。当然,这么做的前提是数组里没有0和负数,不然负负变正了。我们先得提前把数组里的负数和0改成n+1。为啥要n+1呢,因为如果是改成[1,n]里的数,比如1,那我们就没法判断原来数组中1是否真的出现过。

进行完改负数的操作后,我们再遍历一波数组,我们碰上的第一个正数,其所在的下标+1(数组第几位),即是第一个缺失的正整数。如果一个正数都没碰上,说明[1,n]都出现过,那结果就是n+1了。

代码如下:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        for(size_t i = 0;i < nums.size();++i){
            //把0和负数都改成n+1
            if(nums[i] <= 0){
                nums[i] = nums.size()+1;
            }
        }
        for(size_t i = 0;i < nums.size();++i){
            //因为我们会把nums[i]原有的数值改成负数,所以我们使用abs(nums[i])获得原数值
            int val = abs(nums[i]);
            //如果val在[1,n]里,并且它还没被标记过,那我们就去标记它
            if(val <= nums.size() && nums[val-1] > 0){
                nums[val-1] = -nums[val-1];
            }
        }
        //再次遍历,第一个正数的下标+1,就是答案
        for(size_t i = 0;i < nums.size();++i){
            if(nums[i] > 0){
                return i+1;
            }
        }
        //如果没正数,说明[1,n]都齐了,那就是n+1是答案
        return nums.size()+1;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值