【算法分析与设计】第38篇:最近点对与分治在几何中的应用

在第36篇中,我们用分治法解决了凸包问题------将点集按 xx 坐标分为左右两半,递归求解后通过公切线合并。最近点对问题同样适合分治策略,但它的合并步骤远比凸包复杂:左右两半各自的最小距离已知,真正的全局最小距离可能由分居中线两侧的两个点产生。如何在不退化到 O(n2)O(n2) 的前提下,高效检测这种"跨越中线"的最近点对,是算法设计的核心挑战。


一、问题定义与分治框架

设 P={p1,p2,...,pn}P={p1​,p2​,...,pn​} 为平面上的 nn 个点,pi=(xi,yi)pi​=(xi​,yi​)。定义两点之间的欧几里得距离为:

d(pi,pj)=(xi−xj)2+(yi−yj)2d(pi​,pj​)=(xi​−xj​)2+(yi​−yj​)2​

目标是找到使 d(pi,pj)d(pi​,pj​) 最小的一对点。若存在多点重合(距离为零),算法应能正确处理。

分治算法首先将点集按 xx 坐标排序。设排序后的点列为 PxPx​。取 xx 坐标的中位数,将点集分为左半部分 PLPL​ 和右半部分 PRPR​,各含约 n/2n/2 个点。递归求解 PLPL​ 和 PRPR​ 的最近点对距离,分别记为 δLδL​ 和 δRδR​。设 δ=min⁡(δL,δR)δ=min(δL​,δR​)。

若全局最近点对的两点均位于同侧,则递归已将其找出。真正需要处理的是跨越中线的情形:一点在 PLPL​,一点在 PRPR​,它们的距离可能小于 δδ。


二、带状区域合并:正确性与复杂度分析

跨越中线的候选点必然位于中线两侧各 δδ 的带状区域内。若某点距中线的水平距离已超过 δδ,则它到对侧任意点的水平距离也超过 δδ,总距离不可能小于 δδ。因此,仅需考虑落在宽度为 2δ2δ 的带状区域内的点。

将这些点按 yy 坐标排序,记为 Y′Y′。对 Y′Y′ 中的每个点 pp,考虑其上方紧随的若干点,计算它们与 pp 的距离。若距离小于 δδ,更新 δδ。

算法至此似乎仍可能退化:若带状区域中有很多点,按 yy 排序后对每个点逐一检查其后继,最坏情况可能需要检查 O(n)O(n) 个邻居,合并步骤仍是 O(n2)O(n2)。

关键的几何事实拯救了复杂度:在 δ×2δδ×2δ 的矩形内,最多存在常数个点。具体而言,将带状区域中某点 pp 上方 δδ 高度内的区域划分为若干个 δ/2×δ/2δ/2×δ/2 的小方格。每个小方格内至多包含一个点------因为同一个小方格内任意两点的距离不超过 δ/2<δδ/2​<δ,而 δδ 已是左右两侧各自的最小距离,同侧点不可能更近。pp 上方 δδ 高度内最多跨越两行方格,每行至多 66 个方格(宽度 2δ2δ 除以方格宽度 δ/2δ/2 等于 44,但需考虑左右边界各多出一格),因此 pp 只需检查其后的至多 77 个点。

这个常数上界彻底解决了合并步骤的效率问题。对带状区域的每个点,仅需常数次距离计算。带状区域的点按 yy 排序耗时 O(∣Y′∣log⁡∣Y′∣)O(∣Y′∣log∣Y′∣),但若在递归过程中始终维护按 yy 排序的点列(与按 xx 排序的点列一同维护),则合并时的排序可降为 O(n)O(n)------只需将左右两侧已按 yy 排序的序列归并。总递归方程为:

T(n)=2T(n/2)+O(n)T(n)=2T(n/2)+O(n)

解得 T(n)=O(nlog⁡n)T(n)=O(nlogn)。这正是分治法在最近点对问题上达到的最优时间复杂度。


三、算法流程的完整梳理

预处理:将 PP 分别按 xx 坐标和 yy 坐标排序,得到 PxPx​ 和 PyPy​,耗时 O(nlog⁡n)O(nlogn)。

递归过程 ClosestPair(Px,Py)ClosestPair(Px​,Py​):

若 n≤3n≤3,暴力计算所有点对距离,直接返回最小值。

否则,按 xx 中位数 xmidxmid​ 将 PxPx​ 分为 PxLPxL​ 和 PxRPxR​;相应地将 PyPy​ 分为 PyLPyL​ 和 PyRPyR​(保持 yy 排序,仅按 xx 坐标分流)。

递归计算 δL=ClosestPair(PxL,PyL)δL​=ClosestPair(PxL​,PyL​),δR=ClosestPair(PxR,PyR)δR​=ClosestPair(PxR​,PyR​),取 δ=min⁡(δL,δR)δ=min(δL​,δR​)。

构建带状区域点列 Y′Y′:将 PyPy​ 中 ∣x−xmid∣<δ∣x−xmid​∣<δ 的点按原序收集,耗时 O(n)O(n)。

对 Y′Y′ 中每个点 pipi​,依次检查其后至多 77 个点 pi+1,...,pi+7pi+1​,...,pi+7​,计算距离并更新 δδ。

返回 δδ。

整个过程正确且稳定,δδ 在递归归并中不断收紧,带状区域的宽度随之缩小,进一步限制每点的检查数量。


四、高维推广的困难

平面最近点对的 O(nlog⁡n)O(nlogn) 算法漂亮地依赖于一个二维特有的几何事实:在 δ×2δδ×2δ 矩形中,点数的上界是常数。这一事实本身基于"同侧最小距离为 δδ"的前提,限制了点的密集程度。

当问题推广到 dd 维空间时,分治策略是否依然有效?答案是:分治仍然可行,但复杂度的增长方式不同。

在 dd 维空间中,分治策略取某一坐标的中位数将点集分为两半,递归求解后得到 δδ。跨越中线的候选点落在厚度为 2δ2δ 的"平板"区域内。问题在于,在 dd 维空间中,体积为 (2δ)×δd−1(2δ)×δd−1 的区域中可以容纳的点数不再是常数------当 dd 增大时,由于"维度诅咒",点的分布可以更稀疏地保持在相互距离 ≥δ≥δ 的约束下,但仍能填满高维空间的一个薄层。

具体而言,dd 维最近点对的分治算法复杂度为 O(nlog⁡d−1n)O(nlogd−1n)。当 d=2d=2 时,这是 O(nlog⁡n)O(nlogn);d=3d=3 时变为 O(nlog⁡2n)O(nlog2n),尚可接受;但对于高维数据(如机器学习的特征空间,dd 可达数百上千),log⁡d−1nlogd−1n 的因子会使算法失去实用价值。

对于高维最近点对或近似最近邻搜索,实际中更常用的是基于空间划分的启发式方法(如KD树)或局部敏感哈希等近似方法,这些将在第40篇中展开。


五、总结与展望

平面最近点对的分治算法是分治策略在几何问题中的经典应用。它的核心贡献不在于分治框架本身------分治只是将问题规模减半的常规手段------而在于合并步骤中带状区域的分析:通过几何观察将看似需要 O(n2)O(n2) 的跨中线检查压缩至线性时间。常数邻居的论证是几何直觉转化为算法效率的典范。

从凸包到扫描线,再到最近点对,我们反复看到几何算法设计的核心原则:空间的局部性可以严格论证,而非仅凭直觉依赖。这一原则在下一篇------Voronoi图与Delaunay三角剖分------中将以更深刻的方式展现。Voronoi图是最近点对问题的"完全版"结构,它一次性编码了所有点的邻近关系,而Delaunay三角剖分作为其对偶结构,将成为连接计算几何与网格生成、图形学等应用领域的桥梁。

相关推荐
To_OC2 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
用户128526116026 小时前
我把祖传Java项目重构后,接口响应从3s砍到了200ms,只改了这几行代码
java
鱼鱼不愚与6 小时前
《原来如此 | 第01期:为什么导航软件能预测红绿灯倒计时?》
算法
Linsk6 小时前
组件 = 模板 + 业务逻辑
java·前端·vue.js
Nturmoils7 小时前
订单列表慢查询,先看 WHERE、ORDER BY 和 LIMIT
数据库
星沉远浦7 小时前
用Gemini高效解决Java代码报错难以定位的问题
java
用户2986985301411 小时前
Word 文档字符级格式化:Java 实现方案详解
java·后端
复杂网络11 小时前
论最小 Agent 计算机的形态
算法
渣波11 小时前
拒绝 SQL 焦虑!手把手带你用 NestJS + Prisma + DTO 写出“防弹”级后端代码
javascript·数据库·后端
笨鸟飞不快11 小时前
从单个服务到集群:一次完整的性能排查复盘
java·前端