在问 · 算法题解

268. 丢失的数字

简单二分查找排序 + 二分查找力扣原题 ↗

题目描述

输入一个数组 nums,长度为 n。它装着 n 个互不相同的整数,每个都取自 0 到 n 这 n + 1 个数。

因此 0 到 n 中恰好有一个数没有出现。找出并返回这个数。

示例 1
输入:nums = [3,0,1]
输出:2

解释:n = 3,范围是 [0,3],缺的是 2。

示例 2
输入:nums = [0,1]
输出:2

解释:n = 2,范围是 [0,2],缺的是 2。

示例 3
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8

解释:n = 9,范围是 [0,9],缺的是 8。

数据范围

  • n == nums.length
  • 1 <= n <= 10^4
  • 0 <= nums[i] <= n
  • nums 里的数两两不同。

进阶:能否用线性时间、常数额外空间解决?

解题思路:排序 + 二分查找

先排序。没缺数之前,每个位置都满足 nums[i] == i;从缺失的数开始,后面每个数都向左挪了一格,变成 nums[i] == i + 1。于是“nums[i] == i”在下标上呈现“前面全真、后面全假”的单调性,用二分找第一个不满足的位置,它就是缺失的数。

为什么正确

区间 [lo, hi) 之外:lo 左边都满足 nums[i] == i,hi 及右边都不满足;答案始终在 [lo, hi] 中。

复杂度分析

时间 O(n log n)(排序为主,二分只需 O(log n)),额外空间 O(1)(不计排序栈)。

易错点

hi 要从 n(数组长度)开始而不是 n-1:缺失的数可能是 n 本身,此时所有位置都满足 nums[i] == i,答案落在数组之外。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        nums.sort()
        lo, hi = 0, len(nums)
        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] == mid:
                lo = mid + 1
            else:
                hi = mid
        return lo

载入编辑器,自己跑一遍 →

其他解法

排序 + 二分把问题转成“第一个 nums[i] != i 的位置”,核心是二分但受排序拖累;异或和求和都利用 0..n 中恰好缺一个数,一遍扫描得出答案。如果数组本身有序,二分只要 O(log n)。

解法 2:位运算(异或) 时间 O(n),空间 O(1)

把 0 到 n 的所有整数和数组中的所有元素全部异或起来。出现两次的数异或后抵消为 0,最后剩下的就是只在 0..n 中出现、不在数组中的那个数。

线性时间、常数空间,不修改数组,也不会溢出。需要理解异或的抵消性质。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        x = len(nums)
        for i, v in enumerate(nums):
            x ^= i ^ v
        return x

解法 3:数学(求和作差) 时间 O(n),空间 O(1)

0 到 n 的和是 n(n+1)/2。用它减去数组元素之和,差就是缺失的数。

最容易想到的 O(n) 方法,代码极短。数据范围很大时求和可能溢出,这是异或法没有的问题。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        n = len(nums)
        return n * (n + 1) // 2 - sum(nums)

二分查找 专题的其他题目

查看 二分查找 全部题目 →