Skip to content

入门:二分的广义化与三变体速览

基于通用算法概念 · 核于 2026-07

速查

  • 二分的广义本质:不止「有序数组查值」——只要存在一个判定 check(x) 关于 x 单调x 增大时判定结果只朝一个方向变化,如从「可行」单调变「不可行」),就能对 x 二分。单调性 ⇒ 可二分
  • 朴素二分(复习):对下标二分,nums[mid]target 比较,每次把区间砍半,O(log n)。前提是 nums 整体有序。
  • 变体一·旋转数组搜索:整体被旋转(如 [4,5,6,7,0,1,2]),但拆成两段后各自有序。二分时判断 mid 落在哪段、哪半边有序,仍能每轮排除一半,O(log n)。
  • 变体二·二分答案:对答案值域(而非下标)二分。取候选答案 mid,调用 check(mid) 判断是否可行,靠「可行性关于答案单调」折半值域。复杂度 O(log(值域) × 复杂度(check))。
  • 变体三·三分法(Ternary Search):目标函数 f 单峰(先增后减)或单谷时,取 m1 < m2 两内分点,比较 f(m1)f(m2),每次排除左或右三分之一的区间,逼近极值,O(log n)(底数 1.5)。
  • 三变体的共性:都依赖广义单调性——旋转数组靠「部分有序」保证每轮能排除一半;二分答案靠「可行性单调」保证能折半值域;三分靠「凸性」保证能排除一侧。
  • 旋转数组搜索要点:判断 mid 在左段还是右段(nums[lo] <= nums[mid] 则左段有序),再判断目标落不落在这段有序的半边,决定收 lo 还是 hi
  • 找旋转点 = 找最小值:旋转后最小值位置即「断点」。nums[mid]nums[hi] 比较:nums[mid] > nums[hi] 最小在右半,否则在左半。
  • 含重复元素退化nums[mid] == nums[lo](或 == nums[hi])时无法判断哪段有序,只能 lo++ 逐个排除,最坏 O(n)。
  • 二分答案适用条件:①答案有上下界;②check 可在多项式时间完成;③「可行性」关于答案单调(答案越大越难满足,或越小越难)。
  • 二分答案经典题型:「最大化最小值」「最小化最大值」「第 k 小」——如「分裂数组最大值最小化」「木头切割最少得 k 段」「吃香蕉速度」。
  • 三分要点:单峰时 f(m1) < f(m2) ⇒ 极值在 m1 右侧(排除 [lo, m1]),反之排除 [m2, hi];浮点场景要设精度 eps 与足够迭代次数。
  • 三分 vs 二分:二分要「有序离散/可枚举」,三分要「连续凸函数」——多峰或非凸函数三分会陷入局部极值,不适用。
  • 进阶顺序旋转数组搜索二分答案与三分法参考

一、为什么二分能「不止查有序数组」

朴素二分解决的问题是:在整体有序数组里找一个值,每次和中间值比,砍掉不可能的一半。但「有序」只是单调性的一种特例——更本质的判断是:

是否存在一个量 x,使得某个判定 check(x) 的结果随 x 单调变化?

  • 朴素二分x 是下标,check(x)nums[x] >= target。因为 nums 有序,check 随下标单调(一旦 true 就一直 true)。
  • 旋转数组:下标空间上的「有序性」被旋转打破,但「哪半边有序」仍能判断,于是仍能折半。
  • 二分答案x 是候选答案,check(x) 是「这个答案可不可行」。若答案越大越不可行,则 check 单调,可对答案值域二分。
  • 三分x 是自变量,f(x) 单峰时,「极值在 x 左还是右」可以由两个内分点的函数值比较判定。

一旦把二分理解为「单调判定的折半搜索」,就会在很多意想不到的地方发现它能用——这是本叶所有变体的统一视角。

二、三变体速览对比

变体对什么二分单调性来源典型复杂度
朴素二分下标数组整体有序O(log n)
旋转数组搜索下标两段各自有序(部分有序)O(log n)(无重复)/ O(n)(有重复)
二分答案答案值域可行性关于答案单调O(log V × check)
三分法自变量函数单峰/单谷(凸性)O(log n)(底数 1.5)

三、三种变体的共性

  1. 依赖广义单调性:无论形式如何,根本都是「有一侧可以安全排除」。
  2. 每轮排除常数比例:二分排除一半,三分排除三分之一——共同点是几何级数缩区间,所以都是对数级。
  3. 难点在判定(check / 分支条件),不在模板:朴素二分模板极简,难的是旋转数组的分支、二分答案的 check 设计、三分的极值方向判断。

四、与朴素二分的关键区别

  • 朴素二分:判定 nums[mid]target 的大小关系,方向由「大小」直接决定。
  • 旋转数组:判定变复杂——先判断 mid 在哪段、哪半有序,再判断目标在不在那半,是两层判定。
  • 二分答案:判定从「大小比较」换成「调用 check 函数」,搜索对象从「下标」换成「值域」。
  • 三分:判定从「一次比较」换成「两个内分点的函数值比较」,目标从「找定值」换成「找极值」。

下一步

理解了二分的广义化后,先看部分有序如何折半——旋转数组搜索是分支判定最繁的变体,见旋转数组搜索