返回算法课程
搜索基础

二分查找Binary Search

利用有序性,每次比较中点后排除一半不可能包含答案的区间。

22 分钟前置:数组 · 下标 · 有序性

01先从一个问题出发

在按编号排列的书架上找一本书。逐本查看很浪费;查看中间编号,就能决定往哪一半找。

一个具体的例子

在 [2, 5, 8, 12, 16, 23, 38] 中查找 16:先检查 12,排除左半;再检查 23,排除右侧;最后找到 16。

02最直接的办法,为什么慢?

从头到尾逐个比较。

暴力 · O(n)

最多扫描全部 n 个元素,没有利用数组已经有序的信息。

03一步步,推导出优化思路

1

一次比较能告诉我们更多

若中间值小于目标,它左边的全部值也小于目标,可同时排除。

2

维护候选区间

定义闭区间 [left, right]。如果目标存在,它一定还在这个区间内。

3

不断折半直到结束

比较后使用 mid+1 或 mid−1 排除已检查中点。left > right 表示候选区间为空。

贯穿算法的不变量

目标如果存在,始终位于尚未排除的闭区间 [left, right]。

04让每一步,都看得见

试着先预测下一步,再点击「下一步」验证。变量、动画与代码会同步变化。

Binary Search交互演示
STEP 01 / 7
二分演示会先升序排列;排序成本另计
12
0
18
1
27
2
31
3
35
4
43
5
52
6
60
7
当前处理已处理 / 已确定未处理
01

前提:数组升序。候选答案在闭区间 [left, right] 内。

binary_search.py参考实现 · 当前第 2 行
正在载入代码编辑器…

动画展示参考实现的执行轨迹。切换语言时,当前操作会定位到对应代码行。

05复杂度,来自具体的工作量

时间复杂度
O(logn)O(\log n)

每轮长度约除以 2。经历 k 轮后约剩 n/2^k 个元素,因此最多进行 O(log₂ n) 轮比较。

辅助空间
O(1)O(1)

迭代实现只保留 left、right、mid 三个下标,不创建随 n 增长的数组。若先排序,排序成本必须另算。

拖动 n,看看增长速度

06看到什么,应该想到它?

这些特征值得停下来想一想:

关键词只是线索,继续检查适用条件

07这些细节,容易踩坑

01

先明确区间约定

本课采用闭区间,所以 while 是 left <= right。不要和左闭右开模板混用。

02

必须有单调性

未排序数组不能直接二分。实验室为便于观察会先排序,并明确提示。

03

找到不等于找到最左

本算法可返回任何匹配索引。寻找第一个匹配需要在命中后继续收缩右边界。

08把想法带进真实题目

先说出模式和适用条件,再开始写代码。

09不用背模板,回答这几个问题

01为什么可以跳过一整半数组?

02a[mid] < target 时应怎样更新?

03有重复元素时,当前实现保证返回最左索引吗?

你已经走完了这次推导。

能用自己的话解释复杂度和不变量,再标记为掌握。

把知识连起来