2.4 凸包算法
这一篇继续在几何里用分治,问题是凸包(convex hull)。凸包是计算几何最基础也最重要的概念之一——图像处理、碰撞检测、地理信息系统都离不开它。我们会讲两种求法:分治的 ,和工业界更常用的扫描法(Graham 扫描、Andrew 单调链)。它们思路迥异,但都到达 ,而且这个 是最优的(下界也是它)。
1.凸包是什么
给你平面上 个点,想象用一根橡皮筋把它们全箍住,橡皮筋收紧后形成的那个凸多边形,就是这堆点的凸包。严格点说:凸包是包含所有点的最小的凸集。
等价的、更好用的刻画:凸包的顶点是这 个点里的一组子集,使得所有其他点都落在以它们为顶点的凸多边形内部或边界上。换句话说,凸包顶点就是"撑起橡皮筋的"那些点,内部的点不参与。
注意区分"凸包的顶点"和"凸包(多边形区域)"。我们求的是顶点序列。
2.分治法:切两半,各求凸包,再缝合
分治的思路和最近点对(2.3)一脉相承:按 切两半,递归求各自的凸包,再处理"跨边界"的缝合。
CONVEX-HULL-DIVIDE(P)
if n <= 3: 直接返回(三角形/线段)
按 x 中位数切,得左半 L、右半 R
HL = CONVEX-HULL-DIVIDE(L) // 左半凸包
HR = CONVEX-HULL-DIVIDE(R) // 右半凸包
return 合并 HL 和 HR:找上下两条公切线,缝合
合并的关键是找 和 的公切线(common tangent,也叫桥 bridge)。因为 在左、 在右,它们之间能连出两条不穿过任何点的切线——一条上公切线、一条下公切线。把这两条切线之间的凸包边界拼起来,就是合并后的凸包。
找公切线有巧妙的线性算法(沿两个凸包的顶点同步行进),所以合并是 ,递归 。
3.更实用的扫描法:Graham 扫描与 Andrew 单调链
分治法优雅,但工程上更常用的是扫描法,实现更简单、常数更小。两种主流:
Graham 扫描。 先选一个肯定在凸包上的点(比如 最小的,最下面的)。然后按相对这个点的极角排序其余点。用一个栈维护凸包顶点:依次处理排序后的点,每加一个点就检查"栈顶前两个点到这个点是否左转"——如果不是左转(右转或共线),就把栈顶弹掉(它不在凸包上),直到左转为止再压栈。一遍扫描完,栈里就是凸包。
Andrew 单调链。 比 Graham 更简单,不用算极角(算极角要用 atan2,有浮点误差)。它按 坐标排序( 相同按 ),然后分别从左到右、从右到左各扫一遍,分别构建凸包的上半链和下半链,拼起来。判别同样靠"是否左转"的叉积。
ANDREW(P)
按 (x, y) 排序
构建下凸包 lower:依次加入点 p,
while lower 里至少 2 个点且最后两个到 p 不是逆时针(左转):
弹掉 lower 最后一个
压入 p
构建上凸包 upper:从右到左同理
拼接 lower 和 upper(去掉重复的端点)
4.叉积:判转向的核心工具
上面反复说的"左转/右转",靠的是向量叉积。给三个点 ,看 (叉积)的符号:
- 叉积 :从 是逆时针(左转), 在 左侧。
- 叉积 :顺时针(右转)。
- 叉积 :三点共线。
凸包算法全程用叉积判转向:扫描时,如果连续三个顶点"右转"了,说明中间那个点在凸包内部(凹进去的),该弹掉。叉积把几何的"凸性"转化成了代数的符号判断,避免了浮点误差(叉积是整数运算,只要坐标是整数)。这就是为什么凸包算法可以做到精确(无浮点误差)。
5.复杂度与下界
无论分治、Graham 还是 Andrew,复杂度都是 ——瓶颈都在排序那一步(极角排序或 排序)。排序之后,扫描或合并都是 。
而且 是最优的:因为如果能在 求凸包,就能在 排序(把数 当成点,凸包顶点按 序就是排序结果),矛盾于排序的 下界。所以凸包不可能比 更快——这是 4.9 节下界证明思想的一个应用。
6.练习
Q1. Andrew 单调链算法为什么要分"上凸包"和"下凸包"两次扫描,而不是一次扫完?
因为凸包是上下两条凸链组成的。从左到右扫,维护的是下凸包(每步保证左转/凸向下);从右到左扫,维护上凸包。一次扫描无法同时维护上下两条链的性质,所以分两次各管一条,最后拼起来。这个"上下分离"的拆分让算法只需 排序(不用算极角),实现简单且无浮点误差。
Q2. 叉积 的正负各代表什么几何意义?凸包算法怎么用它?
正代表 逆时针(左转, 在 左侧);负代表顺时针(右转);零代表共线。凸包算法扫描时,如果连续三个顶点叉积为负(右转),说明中间顶点是凹的、不在凸包上,弹掉它。叉积把"凸性"转成符号判断,整数运算无误差。
Q3.(思考题) 为什么凸包的下界是 ?给出归约论证。
因为排序可以归约到凸包:把要排序的数 变成平面点 (都在抛物线上)。这些点的凸包包含所有点(都在凸包上),按凸包顶点顺序输出就是按 排序的结果。若凸包能 ,排序就能 ,违反排序的 下界。故凸包 。这是"用一个已知困难问题归约证明新问题下界"的标准手法(第四卷、第八卷的主题)。
7.小结
凸包是计算几何的基石。分治法靠公切线缝合两半凸包,扫描法(Graham/Andrew)靠排序 + 叉积判转向一遍扫出凸包,都到 且最优。叉积把凸性转化成代数符号判断是核心技巧。到这里几何的分治就告一段落——下一篇我们离开几何,进入代数世界,看分治怎么把矩阵乘法从 加速到 。