题目描述

✅ 剑指 Offer 53 - II. 0~n-1中缺失的数字

image-20261001230752579

题意分析

数组升序排列、没有重复元素,并且只缺少一个数。为与代码中的数组长度对应,记 m = nums.length,那么完整的数值范围就是 $[0,m]$,共 $m+1$ 个数,要求找出其中未出现在数组里的那个。

解法:二分查找第一个错位下标

核心思路

[!blue]

设缺失值为 $k$。在它之前没有数字缺失,所以 $i<k$ 时有 nums[i] == i;从它之后开始,元素都向左挪了一位,所以 $i\ge k$ 时有 nums[i] == i + 1。因此,“值是否等于下标”先成立、后不成立,缺失值就是这两个区间的分界,可以二分查找。

用闭区间 [left, right] 保存缺失值的候选范围,初始为 [0, m]。若 nums[mid] == mid,说明缺失值还在中点之后,令 left = mid + 1;否则中点已经错位,缺失值在中点或它之前,令 right = mid。两种更新都保留了答案,又排除了不可能的一半。

当缺失的是 $m$ 时,所有实际元素都与下标对齐,答案仍要落在虚拟位置 $m$,所以初始右端不能只取最后一个下标。循环只在 left < right 时取下中点,此时 mid < right <= m,绝不会读取 nums[m]。

每轮候选区间都会缩小,直到 left == right,唯一剩下的位置就是缺失值。缺失 0 时边界不断向左收缩,缺失 $m$ 时不断向右移动,都不需要特殊分支。

解题步骤

  • 候选答案区间设为 [0,m]。
  • 中点对齐则排除到中点,左端取中点加一。
  • 错位则保留中点,右端收至中点。
  • 返回最终边界下标。

代码实现

class Solution {
    public int missingNumber(int[] nums) {
        // 长度位置也可作为缺失值,实际中点不会访问这个虚拟位置
        int left = 0;
        int right = nums.length;

        while (left < right) {
            int mid = left + (right - left) / 2;

            if (nums[mid] == mid) {
                left = mid + 1;
            } else {
                // 错位中点可能是第一个分界,必须保留
                right = mid;
            }
        }

        return left;
    }
}
func missingNumber(nums []int) int {
    // 长度位置也可作为缺失值,实际中点不会访问这个虚拟位置
    left, right := 0, len(nums)
    for left < right {
        mid := left + (right-left)/2
        if nums[mid] == mid {
            left = mid + 1
        } else {
            // 错位中点可能是第一个分界,必须保留
            right = mid
        }
    }
    return left
}

复杂度分析

  • 时间复杂度:$O(\log(m+1))$,逐轮折半。
  • 空间复杂度:$O(1)$,二分边界。

关键点总结

[!green]

  • 数组长度也可能是缺失值,不能排除。
  • 返回分界本身,不访问对应元素。
  • 数组有序、无重复且只缺一项,才保证每个元素与下标之差只会从 0 变为 1。

易错点总结

[!yellow]

  • 右端只取最后下标,会漏掉缺失末尾。
  • 错位时丢掉中点,可能跳过最早错位处。
  • 返回 nums[left] 会错一位或越界。

相似题目

题目 难度 关联与区别
1060. 有序数组中的缺失元素 中等 都二分第一个缺失计数达到目标的位置;本题用 nums[i] - i 表示从零开始的缺失量,该题改为相对首项的缺失量并查询第 k 个。
1539. 第 k 个缺失的正整数 简单 都利用有序数组中实际值与应有位置之差形成单调缺失计数;该题再按 k 定位边界并补上剩余缺失量。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/73524399
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!