形式陈述
设数组 理路 数组 Array 以连续整数下标支持随机访问的有限序列结构。 A [ 0. . n ) 依某全序 理路 全序 Total order · Linear order 任意两个元素都可比较的偏序。 非降排列,目标为 x 。下标从 0 开始,半开区间 [ ℓ , r ) 包含 ℓ 而不包含 r 。精确查找从 ℓ = 0 , r = n 出发,只要 ℓ < r 就取
m = ℓ + ⌊ r − ℓ 2 ⌋ . 若 A [ m ] < x ,令 ℓ = m + 1 ;若 A [ m ] > x ,令 r = m ;若相等则返回 m 。当 ℓ = r 而尚未命中时,返回“不存在”。其循环不变式 理路 循环不变式 Loop invariant 在循环初始化后成立,并在每次循环迭代后继续成立的断言。 是:若目标存在,则当前区间仍包含至少一个目标位置。排序保证每次删去的部分不含目标,区间严格缩短保证算法终止。n ≥ 1 时至多比较 ⌊ log 2 n ⌋ + 1 次;空数组直接结束,不需计算 log 0 。
求分界位置时应换一个明确接口。设 P ( 0 ) , … , P ( n − 1 ) 先假后真,并约定虚拟哨兵 真 P ( n ) = 真 。仍从 [ 0 , n ) 出发,但 P ( m ) 为真时令 r = m ,为假时令 ℓ = m + 1 ;终止后返回 ℓ 。不变式为“ℓ 之前全假,自 r 起全真”,因此返回值是第一个真位置;若实际位置全假则返回 n 。取 P ( i ) ≡ A [ i ] ≥ x 就得到 lower bound,算法从不读取不存在的 A [ n ] 。
直觉
排序把一次比较从“关于一个元素的信息”升级为“关于一整段的信息”:既然 A [ m ] < x ,那么 A [ 0. . m ] 整段都小于 x ,一次比较排除一半候选,这正是猜数游戏每问砍半的策略。信息论图像在单调谓词版本中最精确:每次询问 P ( m ) 只返回真或假一比特,而答案是 n + 1 个可能分界位置之一,故最坏至少需要 ⌈ log 2 ( n + 1 ) ⌉ 次询问,二分达到同一数量级。精确查找时比较 A [ m ] 与 x 有小于、等于、大于三种结果,不能逐字说成“一次比较只有一比特”;但受有序结构约束的比较决策树仍给出 Θ ( log n ) 的最坏界,二分同样在渐近意义下最优。
实现时要始终保持同一套区间约定:区间开闭的语义、中点归入哪一半、每步是否严格缩小区间、重复元素时返回哪一个,都应由所选不变式决定。混用闭区间与半开区间的更新式,容易漏掉端点或无法终止。定宽整数下 ℓ + r 还可能溢出,稳妥写法是 m = ℓ + ⌊ ( r − ℓ ) / 2 ⌋ 。
图片加载失败 二分查找半开区间收缩示意图
例子与边界
在 [ 1 , 3 , 3 , 7 , 9 ] 中查找 7 :ℓ = 0 , r = 5 ,m = 2 ,A [ 2 ] = 3 < 7 ,故 ℓ = 3 ;m = 4 ,A [ 4 ] = 9 > 7 ,故 r = 4 ;m = 3 ,A [ 3 ] = 7 命中——三次比较,而逐项扫描最坏需五次。若改问“第一个不小于 3 的位置”,答案是下标 1 ;其区间依次是 [ 0 , 5 ) → [ 0 , 2 ) → [ 0 , 1 ) → [ 1 , 1 ) :两个等于 3 的中点都使右端左移,最后排除 A [ 0 ] = 1 。此时必须改用单调谓词版的不变式;“相等即停”的版本只保证返回某个出现位置,不承诺首个或最后一个。固定中点与比较规则后,它的返回位置仍是确定的。
未排序的数组没有“排除一半”的依据:在 [ 2 , 9 , 4 ] 中查找 4 ,中点 A [ 1 ] = 9 > 4 会把算法引向左半 [ 2 ] ,与真实位置失之交臂——排序假设一旦缺失,算法安静地给出错误答案而非报错。复杂度结论还依赖 O ( 1 ) 随机访问:链表上定位中点还需指针遍历;谨慎复用区间边界可把总指针移动控制为 O ( n ) ,却不会得到数组上的 O ( log n ) 总时间。若键比较特别昂贵,减少比较次数仍可能有用。区间必须每步严格缩小:闭区间写法中若用 m = ⌊ ( ℓ + r ) / 2 ⌋ 搭配 ℓ ← m ,则在 r = ℓ + 1 时 m = ℓ 、区间原地不动,循环永不终止。
从优化阈值到最优见证 理路 从优化阈值到最优见证 Optimization to threshold decision · Binary search for optimum · 优化搜索判定归约 在有限整数目标和显式上界下二分求最优值,再用受阈值约束的前缀恢复见证,分开核算数值范围与编码成本。 明确补足答案二分的两个接口:数值范围 [ 0 , U ] 只需 O ( log ( U + 1 ) ) 次判定,但查询编码与判定成本仍收费;得到最优值后,还要通过受该阈值约束的前缀或问题特有自归约,恢复真正达到它的对象。
推论与应用
单调谓词形式把二分从查表扩展为“答案二分”:只要可行性关于参数单调(容量越大越可行之类),就能用对数次可行性判定逼出最优值,这是把判定器升级为优化器的通用手法。连续版本是实数区间上的对分法,依托介值定理 理路 介值定理 Intermediate value theorem 连续函数在实区间上不能跳过中间高度;由完备性证明存在性,并厘清二分法、唯一性和不动点迭代。 逼近方程的根,每步把包围根的区间长度减半。结构化版本是二叉搜索树 理路 二叉搜索树 Binary search tree · BST 每个结点左子树键小于、右子树键大于该结点键的二叉树。 ,把中点比较固化为指针分叉以支持动态插删。而“每次比较至多一比特信息”的计数论证,同样是比较排序下界 理路 比较排序下界 Comparison sorting lower bound 任何最坏情形比较排序都需 Ω(n log n) 次比较。 的核心。
若许多单调判定都依赖同一份更新历史,批量二分 理路 批量二分 Parallel binary search · 离线批量二分 · 并行二分 把多个单调查询的二分中点按更新日期分桶,每轮从同一初态重放一次,以共享判定状态求各自首次达标时间。 把各自中点按日期分桶,每轮只从初态顺扫重放一次。每条查询仍维持本页的首真边界;共享的是昂贵的判定状态,不是用离线条件替代单调性,也不要求多处理器。
本页的 O ( log n ) 是有序数组、常数时间随机访问和比较模型下的最坏查询界。前驱问题 理路 Word-RAM 前驱问题 Predecessor problem 在 Word-RAM 的有限整数宇宙中维护有序集合,并查询不大于给定键的最大成员。 把“找最后一个不超过 x 的键”提升为可跨模型比较的接口,整数 Word-RAM 能利用键的 bit 结构突破普通比较树路线;分数级联 理路 分数级联 fractional cascading 在相关有序目录间建立采样桥,使一次完整二分后可常数时间转移位置。 在许多相关有序表之间复用一次定位,把重复二分的成本压到一次对数加线性表数。若数据主要驻留外存,缓存无关搜索树 理路 缓存无关搜索树 cache-oblivious search tree 在程序不知道块大小与缓存容量时,通过递归布局使搜索在各级存储上同时获得对数块传输界。 关注的是块传输而非比较次数。三者都从二分的分界思想出发,却改变了问题批次或机器模型。
galloping归并 理路 短段驱动的galloping归并 Galloping merge · Exponential-search merge · 跳跃归并 · 短段驱动归并 以较短有序段为驱动,用指数探测和二分批量越过长段前缀,证明稳定边界及按跳跃长度计费的比较界。 从上一次边界按偏移0、1、3、7、…探测,夹住目标后再运行本页二分。若这次只跨过d项,比较数是O(1+log(d+1)),而非每次都按整个长段长度计费;相等键使用lower还是upper边界,由原左记录必须先出的稳定合同决定。
参考资料
Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022, §2.3 and exercises。
Donald E. Knuth, The Art of Computer Programming, Vol. 3: Sorting and Searching, 2nd ed., Addison-Wesley, 1998, §6.2.1。
Robert Sedgewick and Kevin Wayne, Algorithms , 4th ed., 2011,§1.1 配套 BinarySearch.java ,可与本页半开区间版本对照。