第6章|二分、堆与优先队列
Unit 01 · LeetCode #704
二分不是反复猜中点,
而是反复证明一整段不可能。
每轮比较都必须维护一个可信承诺:若目标存在,它仍位于当前闭区间中。只有当比较结果能够证明某一整段全部过小或全部过大时,才可以删除这一段。
题目要求
给定一个按严格递增顺序排列的整数数组 nums 和目标值 target。若目标存在,返回其下标;否则返回 -1。要求算法时间复杂度为 O(log n)。
约束
1 ≤ nums.length ≤ 10⁴;- 数组中的所有整数互不相同并按升序排列;
- 若目标不存在,必须返回
-1。
示例
输出:4
输出:-1
本页主方法
闭区间迭代二分:初始化 left=0、right=n-1,循环条件为 left≤right。每轮排除至少一个位置,时间复杂度 O(log n),额外空间 O(1)。
与 Unit 00 的联系:从概念地图进入第一个完整证明
识别候选空间、单调证据、区间契约与严格推进。
把抽象概念落实为每一轮可验证的“比较—排除—保持不变量”。
[left,right]中的每个位置仍可能是目标;区间外位置已被证明不可能。
当前区间内用于获取排除证据的位置,而不是一个“随机猜测”。
未命中时,新区间长度必须小于旧区间,否则循环可能永不终止。
候选规模连续减半:n → n/2 → n/4 → … → 1,轮数约为 log₂n。
比较nums[mid]与target,决定哪一整段可以安全删除。
区间演化记录
核心不变量
若目标存在,它始终位于当前闭区间 [left,right] 内;区间外元素已被比较结果证明不可能。
更新判断
先判断mid是否命中;未命中时,再判断mid及其哪一侧可以整体排除。
线性扫描每次通常只排除当前一个位置;二分比较利用升序关系,一次排除连续半段。
在相邻区间中,向下取整的mid可能等于left。如果未命中后仍令left=mid,候选区间将完全不变。
代码正确不仅因为示例能运行,还因为初始化、每轮更新和退出条件共同维护同一个不变量。
目标若存在,初始时必在完整数组[0,n−1]中。
根据升序和比较结果,只删除已证明不可能的一侧。
每轮严格缩小;区间为空时,所有位置均已被排除。
(left+right)//2 不会发生整数溢出;使用 left+(right-left)//2 仍能更直接表达“从left走半个区间”。这不是重新背一份二分代码。你需要识别:数组消失以后,候选空间仍是闭区间;比较证据从 nums[mid] 改为外部接口 guess(mid)。
官方题意
系统在整数 1..n 中固定选择一个秘密数字 pick。每次调用 guess(num) 会返回:
-1:你的猜测过大,即num > pick;1:你的猜测过小,即num < pick;0:猜中。
官方示例与约束
1 ≤ n ≤ 2³¹−1;1 ≤ pick ≤ n;pick在整个游戏中保持不变。
guess(mid) == -1 表示什么?[1,n] 开始?| 结构维度 | #704 二分查找 | #374 猜数字 |
|---|---|---|
| 候选对象 | 数组下标 0..n−1 | 整数值 1..n |
| 获取证据 | 读取 nums[mid] | 调用 guess(mid) |
| 命中条件 | nums[mid] == target | guess(mid) == 0 |
| 目标更大 | nums[mid] < target | guess(mid) > 0 |
| 目标更小 | nums[mid] > target | guess(mid) < 0 |
result > 0 时如何更新?官方样例:n=10,pick=6
不会自动跳转。可以继续重放失败与命中轨迹,或手动进入 Unit 02。
第6章 Unit 01 Skill卡
闭区间二分的本质是维护候选集合:中点比较提供排除证据,更新式删除已经被证明不可能的位置,并确保区间严格缩小。你还把相同契约迁移到了没有数组的整数搜索空间。