LeetCode每日一题(2020.10.16)  268. 丢失的数字 2020.10.17补

LeetCode每日一题(2020.10.16)   2020.10.17补

最难做的事,就是把容易做的事持之以恒。

今日题目:

268. 丢失的数字

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

进阶:

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

示例 1:

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


示例 2:

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

提示:

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

通过次数91,470,提交次数159,375

首先,给出我自己写的代码:

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        n=len(nums)
        if 1 <= n <=10000:
            for i in range(0,n+1):
                if nums.count(i)==0:
                    return i

这个的实现效果并不好,执行时间和内存占用都比较高:

官方给出了好几种解题方案,我们不妨一一学习一下!

方法一:排序
分析:

如果数组是有序的,那么就很容易知道缺失的数字是哪个了。

算法:

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

代码:

class Solution:
    def missingNumber(self, nums):
        nums.sort()

        # Ensure that n is at the last index
        if nums[-1] != len(nums):
            return len(nums)
        # Ensure that 0 is at the first index
        elif nums[0] != 0:
            return 0

        # If we get here, then the missing number is on the range (0, n)
        for i in range(1, len(nums)):
            expected_num = nums[i-1] + 1
            if nums[i] != expected_num:
                return expected_num

复杂度分析

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

方法二:哈希表
分析

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

算法

我们将数组中的所有数插入到一个集合中,这样每次查询操作的时间复杂度都是 O(1) 的。

代码:

class Solution:
    def missingNumber(self, nums):
        num_set = set(nums)
        n = len(nums) + 1
        for number in range(n):
            if number not in num_set:
                return number

复杂度分析

时间复杂度: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,因此可以用一次循环完成所有的异或运算,例如下面这个例子:

下标    0    1    2    3
数字    0    1    3    4
可以将结果的初始值设为 nn,再对数组中的每一个数以及它的下标进行一个异或运算,即:
=4∧(0∧0)∧(1∧1)∧(2∧3)∧(3∧4)
=(4∧4)∧(0∧0)∧(1∧1)∧(3∧3)∧2
=0∧0∧0∧0∧2
=2

就得到了缺失的数字为 2。

代码:

class Solution:
    def missingNumber(self, nums):
        missing = len(nums)
        for i, num in enumerate(nums):
            missing ^= i ^ num
        return missing

复杂度分析

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

方法四:数学
分析

我们可以用 高斯求和公式 求出 [0..n] 的和,减去数组中所有数的和,就得到了缺失的数字。高斯求和公式即

算法

我们在线性时间内可以求出数组中所有数的和,并在常数时间内求出前 n+1 个自然数(包括 0)的和,将后者减去前者,就得到了缺失的数字。

代码:

class Solution:
    def missingNumber(self, nums):
        expected_sum = len(nums)*(len(nums)+1)//2
        actual_sum = sum(nums)
        return expected_sum - actual_sum

复杂度分析

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/missing-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值