LC448. 找到所有数组中消失的数字

题目

给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。

提示:

n == nums.length
1 <= n <= 105
1 <= nums[i] <= n

进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。

思路

哈希

如果单纯为了AC,不考虑空间复杂度,采用哈希集合是一个很好的办法。

  1. 首先用一个循环将数组nums中出现的数采用unordered_set记录下来
  2. 因为需要找到没在[1, n]中的数,因此可以采用遍历1到n这n个数,如果没有在set中,则说明没有出现过

时间复杂度:O(n)
空间复杂度:O(n)

原地处理

降低空间复杂度的一般方式为通过牺牲时间复杂度,可以进行排序再筛选。但是题目中要求时间复杂度为O(n),则不能考虑排序方式。
根据的题目的局限性,此时只有一个办法——在数组nums内解决问题,即原地处理。
但这种不能采用其他外加数据结构怎么进行内部筛选呢?
可以发现nums按照规定应该是[1, n]这n个数字,正好对应数组下标index+1,或许可以通过此处解决问题。
既然是对应关系,那就很像哈希了,因此我们可以将数组采用哈希映射的思想:

  1. 未出现的数我们不知道是哪些,但是出现的数我们可以像set一样标记出来。因为需要原地操作,所以我们可以将将出现的值与下标对应起来,比如若x这个值出现了,把nums[x-1]设定为一个特定值,这样所有出现的值就都标记清楚了。
  2. 但是这样的话,遍历数组nums[i]时i索引到x-1时,nums[x-1]原本的值我们就不知道了,所以还需另想办法。此时我们可以将出现过数组的值x,如果他在n内,就将nums[x-1]加一个固定值。当遍历nums索引到nums[x-1]想看它原本的值时,可以将这后加的固定值去掉。这样既可以标记出现的数,也不会完全覆盖数组内容。因为要找出[1,n]的值,所以可以将这个后加的固定值设为n(或者大于n的数),标记数x后,nums[x]一定大于n,此时就可以寻找没出现数。

在这里插入图片描述

步骤如下:

  1. 遍历数组,对于x = nums[i]:如果x不大于n,直接将nums[x-1]+=n;如果x大于n,将x依次减n,直到x不大于n,将nums[x-1]+=n。
  2. 再一次遍历数组,如果数组中nums[i]不大于n,将i+1记录下来,这是没出现过的数。
    如上图所示,只有下标为1的数不大于n(此处为3),说明2(即i+1)是缺少的数

代码

哈希
class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        unordered_set<int> uset;
        vector<int> ans;
        int len = nums.size();
        for(int i=0;i<len;i++){
            if(!uset.count(nums[i]))
                uset.emplace(nums[i]);
        }
        for(int i=1;i<=len;i++){
            if(!uset.count(i))
                ans.push_back(i);
        }
        return ans;
    }
};

原地数组

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        int len = nums.size();
        vector<int> ans;
        for(int i=0;i<len;i++){
            int m =nums[i]-1;
            while(m>=len)
                m-=len;
            nums[m]+=len;
        }
        for(int i=0;i<len;i++){
            if(nums[i]<=len)
                ans.push_back(i+1);
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值