每日一题2023.11.3|41.缺失的第一个正数

LeetCode41.缺失的第一个正数

链接
参考解答

哈希表

如果对空间复杂度没有要求,则可以有以下解法

  1. 首先,将给定数组进行排序,然后顺序遍历数组找到第一个缺失的正数。对于排序时间复杂度为O(nlogn),空间复杂度为O(logn) ,对于顺序遍历时间复杂度为O(n)。总体来说时间复杂度为O(nlogn),空间复杂度为O(logn)
  2. 从1开始对于每一个正整数,顺序遍历数组看其是否缺失。时间复杂度为O(n^2)空间复杂度为O(1)
  3. 对第二种方法进行优化。将数组中的元素映射在哈希表中,从1开始遍历正数看其是否在哈希表中,找到缺失的第一个正数。时间复杂度为O(n),空间复杂度为O(n)
  4. 以上三种方法都不满足题目的要求
    退一步来看,只有第三种方法最接近,只需要在第三个方法的基础之上优化时间复杂度即可。
    先来说说为什么要使用哈希表,因为将数据存储在哈希表中可以减少由于顺序遍历带来的时间复杂度。

那就考虑以下能不能在原数组的基础上进行哈希,也就是原地哈希。
缺失的第一个正数一定是位于范围[1,n+1]内,当数组中的数都为正数并且都按照顺序出现,那么缺失的第一个正数是n+1;
顺序遍历数组中的元素,将num映射在下标为num-1的位置处,遍历完之后,哪个位置没有打上标记,那个位置加1就是缺失的第一个正数。接下来细说怎么打标记。

  1. 由于寻找的是第一个未出现的正数,所以可以用负数作为标记。从头到尾遍历数组,将负数变为n+1(其实任何一个大于等于n+1的正数都可)
  2. 将当前元素num,映射在下标为num-1的位置处。但是在映射的过程中,有可能会将当前元素的后边的元素变为负数,所以需要先取num的绝对值,然后将下标为num-1的位置的数值变为负数,但是需要注意的是,位置上原本就是负数不需要重复标记,所以需要先取abs(nums[num-1])再进行标记
  3. 从头到尾遍历数组,找到第一个大于0的位置in,返回in+1。如果都小于0,则返回n+1;
class Solution {
public:
//哈希表原地哈希
    int firstMissingPositive(vector<int>& nums) {
        //首先遍历数组,将负数转换为nums.size()+1
        int n=nums.size();
        for(int &num:nums) if(num<=0) num=n+1;
        //第二步,进行哈希映射。将nums[i]映射在位置nums[i]-1处(下标的位置)
        for(int i=0;i<n;++i){
            int num = abs(nums[i]);
            if(num<=n) nums[num-1] = -abs(nums[num-1]);
        }
        //第三步,寻找第一次出现的正数
        for(int in=0;in<n;++in){
            if(nums[in]>0) return in+1;
        }
        return n+1;
    }
};

置换

除了打标记之外,还可以使用置换的方式,
如果数组中包含x属于[1,N],将其置换到位置x-1

  • 对数组进行遍历,对于遍历到的数值x=nums[i],如果x属于[1,N]则将其与num[x-1]进行置换,这样x就出现在了应该出现的位置。
  • 完成一次交换之后,新的nums[i]可能还在[1,N]之内并且不在合适的位置上,需要再次进行交换。直到nums[i]的值出现在nums[i]-1的位置上
  • 每次交换操作都会使得某一个数交换到合适的位置,交换的次数最多为N次,时间复杂度为O(N),空间复杂度为O(1)
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        //置换
        for (int i=0;i<nums.size();++i){
            //nums[i]在[1,N]的范围内
            while(nums[i]>0&&nums[i]<=nums.size()&&nums[i]!=nums[nums[i]-1] ){
                swap(nums[i],nums[nums[i]-1]);

            }
        }
        for (int i=0;i<nums.size();++i) {
            if (nums[i]!=i+1) return i+1;
        }
        return nums.size()+1;
    }
};

这里有一个需要注意的地方就是,for循环中的while循环的判断条件,我第一次写的判断条件是"nums[i]!=i+1"也就是nums[i]这里存储的值应该是"i+1"不是的话就置换,但是发生了时间超限,
对于测试用例“[1,1]”来说,会在第二个元素那里会无限的进行交换,所以产生了时间超限。
应该将其改为"nums[i]!=nums[nums[i]-1]"下标为i位置的元素与与其应该出现的位置的元素不一致时才交换。

while(nums[i]>0&&nums[i]<=nums.size()&&nums[i]!=nums[nums[i]-1] )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值