V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

二分答案

查找 · 答案空间上的二分 · 最小可行解

数组没了,也能二分

经典题:4 堆香蕉 [3, 6, 7, 11],警卫 8 小时后回来,珂珂每小时只能吃一堆里的 k 根——最小时速 k 是多少?没有现成的数组可查,但把候选答案排成一排(速度 1..11),奇妙的事发生了:速度越快越可能吃完,可行性关于答案单调—— ✗ ✗ ✗ ✓ ✓ ✓ …。「找第一个 ✓」,这不就是二分边界里的 lower_bound 吗?

柱子这回是答案本身

下面的柱子不是被查数组,而是 11 个候选速度。每一步试探 mid:现场算 Σ ⌈pile / k⌉ 与 8 比——可行就「答案还能更小」收 hi,不可行就「只能加速」抬 lo;相遇点即最小可行速度 4(恰好 8 小时,慢一档就 10 小时超时)。右侧代码随每一步同步高亮。

1
2
3
4
5
6
7
8
9
10
11

数组没了也能二分!4 堆香蕉 [3, 6, 7, 11],8 小时内吃完的最小时速是多少?把候选答案排成一排——这 11 根柱子就是速度 1..11。速度越快越可行(单调 ✗✗✗✓✓…),找「第一个可行」即可

1function minEatingSpeed(piles: number[], h: number): number {
2 let lo = 1, hi = Math.max(...piles); // 答案空间 [1, max]
3 while (lo < hi) {
4 const mid = (lo + hi) >> 1; // 试一个答案
5 if (canFinish(piles, mid, h)) hi = mid; // 可行:答案还能更小
6 else lo = mid + 1; // 不可行:只能加速
7 }
8 return lo; // 最小可行速度
9}
10// canFinish(k):Σ ceil(pile / k) <= h —— k 越大越可行(单调谓词)
香蕉堆[3, 6, 7, 11]
限时 h8 小时
候选速度 [lo, hi][1, 11]
mid—
1 / 7

什么时候能「二分答案」

三要素:①答案空间有界(如 [1, max]);②可行性关于答案单调;③单点可行性可验(一个 O(n) 的 check)。
复杂度:O(check × log(答案空间))——把「求最优」降成「验可行」。
找最大可行:谓词方向反过来(✓✓✓✗✗),找最后一个 ✓ = upper 侧模板。
实数版:答案是实数时二分固定次数(如 100 轮)或到精度 ε。

「最小化最大值 / 最大化最小值」一族几乎全是它:分割数组的最大段和最小化、D 天内送达的最低运载能力、切木材求最长段、会议室最少数量……凡是答案越大越可行(或越不可行)的题,先想二分答案。至此查找大类四页收官:找值 → 找边界 → 断崖找值 → 找答案——二分的四重境界。