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
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

最近点对(分治)

计算几何 · 分治 + δ 带合并 · O(n log n)

离得最近的两个点

平面上 n 个点,找距离最近的两个——与最远点对正好对偶。暴力比较所有点对是 O(n²);但「最近」没有「必在凸包上」这样的免费午餐(最近的点往往藏在内部),需要另一件武器:分治。

分而治之 + δ 带

把点按 x 排序,沿中线切成左右两半,各自递归求最近距离,取 δ = min(左, 右)。麻烦在合并:最近的一对可能一只脚在左、一只脚在右。但注意——这样的点对两端都必须离中线不到 δ,所以只需检查中线两侧 δ 宽的带。更妙的是把带内的点按 y 排序后,每个点只需和 y 差小于 δ 的邻居比较:一个 δ×2δ 的矩形里最多塞得下 8 个相距 ≥δ 的点(鸽笼原理),所以每点只比常数次。合并 O(n),整体 T(n) = 2T(n/2) + O(n) = O(n log n)。

下面固定 8 个点。点「下一步」逐步看:紫色虚线是分治中线、左右两半各自求出最近对(绿线)定出 δ,然后浅紫色带亮起——带内按 y 序逐对比较(蓝色虚线),本例连刷两次:先 1.581、再 1.118——最终答案恰好跨越中线,正是 δ 带存在的意义。右侧代码随每一步同步高亮。

平面上 8 个点(已按 x 排序),找距离最近的两个。暴力 O(n²),分治能到 O(n log n)

1function closest(pts: Pt[]): number { // pts 已按 x 排序
2 if (pts.length <= 3) return brute(pts);
3 const mid = pts.length >> 1;
4 const midX = (pts[mid - 1].x + pts[mid].x) / 2; // 中线
5 const dl = closest(pts.slice(0, mid)); // 左半递归
6 const dr = closest(pts.slice(mid)); // 右半递归
7 let d = Math.min(dl, dr); // δ
8 const strip = pts.filter(p => Math.abs(p.x - midX) < d)
9 .sort((a, b) => a.y - b.y); // δ 带内按 y 排序
10 for (let i = 0; i < strip.length; i++)
11 for (let j = i + 1; j < strip.length &&
12 strip[j].y - strip[i].y < d; j++) // 只比 y 差 < δ 的近邻
13 d = Math.min(d, dist(strip[i], strip[j])); // 跨带可能刷新
14 return d;
15}
点数8
当前最近-
1 / 10

为什么合并只要 O(n)

分治:中线分半,δ = min(左最近, 右最近)。
δ 带:跨线的更近对两端必落在 |x − 中线| < δ 的带内。
鸽笼:带内按 y 排序,每点只需比 y 差 < δ 的至多 7 个邻居。
复杂度:T(n) = 2T(n/2) + O(n) = O(n log n)。

最近点对是「分治 + 有序合并」在几何上的招牌应用,同一套思想还支撑着归并排序、逆序对计数、平面最近异色点对等问题。它也提醒我们:分治的难点常常不在「分」,而在合并时如何不漏掉跨越边界的答案——δ 带就是那个精确的补网。