268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

示例 1:

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2是丢失的数字,因为它没有出现在 nums 中。
示例 2:

输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2是丢失的数字,因为它没有出现在 nums 中。
示例 3:

输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围[0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:

输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1是丢失的数字,因为它没有出现在 nums 中。

提示:
n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
nums 中的所有数字都 独一无二

方法一:排序
首先我们对数组进行排序,随后我们可以在常数时间内判断两种特殊情况:0 没有出现在数组的首位,以及 n 没有出现在数组的末位。如果这两种特殊情况都不满足,那么缺失的数字一定在 0 和 n之间(不包括两者)。此时我们可以在线性时间内扫描这个数组,如果某一个数比它前面的那个数大了超过 1,那么这两个数之间的那个数即为缺失的数字。

var missingNumber = function(nums) {
    let n =nums.length;
    let arr = nums.sort((a,b) => a-b);
    if(nums[0] !== 0) return 0;
    if(nums[n-1] !== n) return n;
    for(let i = 0;i<n;i++){
        let num = nums[i] + 1;
        if(arr[i+1] !== num) return num;
    }
};

复杂度分析

  • 时间复杂度:O(nlogn)。由于排序的时间复杂度为O(nlogn),扫描数组的时间复杂度为 O(n),因此总的时间复杂度为O(nlogn)。
  • 空间复杂度:O(1)或O(n)。空间复杂度取决于使用的排序算法,根据排序算法是否进行原地排序(即不使用额外的数组进行临时存储),空间复杂度为 O(1) 或O(n)。

方法二:哈希表
我们可以直接查询每个数是否在数组中出现过来找出缺失的数字。如果使用哈希表,那么每一次查询操作都是常数时间的。

var missingNumber = function(nums) {
    let map = new Map();
    for(let num of nums){
        map[num] = true;
    }
    for(let i=0;i<=nums.length;i++){
        if(!map[i]) return i;
    }
};

复杂度分析

  • 时间复杂度:O(n)。集合的插入操作的时间复杂度都是O(1),一共插入了 n 个数,时间复杂度为O(n)。集合的查询操作的时间复杂度同样是 O(1),最多查询 n+1 次,时间复杂度为O(n)。因此总的时间复杂度为O(n)。
  • 空间复杂度:O(n)。集合中会存储 n个数,因此空间复杂度为 O(n)。

方法三:位运算
由于异或运算(XOR)满足结合律,并且对一个数进行两次完全相同的异或运算会得到原来的数,因此我们可以通过异或运算找到缺失的数字。

我们知道数组中有 n个数,并且缺失的数在 [0…n] 中。因此我们可以先得到[0…n] 的异或值,再将结果对数组中的每一个数进行一次异或运算。未缺失的数在 [0…n] 和数组中各出现一次,因此异或后得到 0。而缺失的数字只在[0…n] 中出现了一次,在数组中没有出现,因此最终的异或结果即为这个缺失的数字。

在编写代码时,由于 [0…n] 恰好是这个数组的下标加上 n,因此可以用一次循环完成所有的异或运算。可以将结果的初始值设为 n,再对数组中的每一个数以及它的下标进行一个异或运算。

var missingNumber = function(nums) {
    let n = nums.length;
    let res = n;
    for(let i = 0;i<n;i++){
        res ^= nums[i] ^ i;
    }
    return res;
};

复杂度分析

  • 时间复杂度:O(n)。这里假设异或运算的时间复杂度是常数的,总共会进行 O(n) 次异或运算,因此总的时间复杂度为 O(n)。
  • 空间复杂度:O(1)。算法中只用到了O(1) 的额外空间,用来存储答案。

方法四:
假设把缺失的数组补上,再排序,就是一个连续的从0-n的数组(这里的n就是原本数组长度)。计算连续 m ~ n 的数字的总和(m < n, 且都是正整数),公式 sum = (m+n) * (n - m + 1) / 2。再基于目前给出的数组都是从0开始计数的,公式为sum = n * (n + 1) / 2。我们在线性时间内可以求出数组中所有数的和,并在常数时间内求出前n+1 个自然数(包括 0)的和,将后者减去前者,就得到了缺失的数字。

var missingNumber = function(nums) {
    let n = nums.length;
    let sum = n*(n+1)/2;
    let res = 0;
    for(let num of nums){
        res += num;
    }
    return sum - res;
};

复杂度分析

  • 时间复杂度:O(n)。求出数组中所有数的和的时间复杂度为O(n),高斯求和公式的时间复杂度为 O(1),因此总的时间复杂度为 O(n)。
  • 空间复杂度:O(1)。算法中只用到了O(1) 的额外空间,用来存储答案。

类似题目
数组中消失的数字
数组中重复的数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值