lec2 中学习的是如何把一个多边形切割开来;而这节课学习的就是,如何把一堆点给包起来
就像你在地上撒了一把钉子,然后用一根橡皮筋把它们围起来,拉紧之后那个形状就是 “凸包”。
所以,构造凸包的这个过程,我们还是从暴力解法开始
朴素暴力解法
我们可以假定一个结论:将点集 S 中的所有点遍历一遍,然后将这些三角形中包住的点都删除;最后剩余的这些没有被包围的点,一定是凸包的顶点。(作为三角形顶点或位于三角形边上的点不被删除)
那么我们就可以
#算法步骤(CH1):遍历 中所有可能的三元组 :一共 种组合。
对每个组合 ,遍历点集中每个点 。
检查 是否落在三角形 内部。
如果是,就把 从 中删除。
最后剩下的点就是 CH 顶点,把它们按极角排序后连接成凸包。
总时间复杂度:
接下来,我们可以对这个算法进行一定的优化
2. 优化算法
我们从另一个角度考虑,S 中的所有点,必然位于凸包的边 xy-> 有向直线的右侧,故我们不需要对三维的所有点进行遍历,只需要对二维的点进行遍历即可,然后判断是否其余所有点都位于他们的右侧
算法步骤(CH2)解释:
3. 遍历所有有序点对 (注意是有序的)
假设该边合法(valid ← true)
对其它点 遍历判断:
- 判断 是否在直线 的左侧(说明不合法)
如果发现一个点在左侧,就将 valid 设置为 false
最后,如果 valid 仍为 true,说明 是 CH 的边之一
加入这条边到 CH 边集
所有边加入完毕后,排序构造完整凸包
总时间复杂度:
接下来,看看实际当中用的比较多的三种优化算法。
在讲算法之前,老师首先对前面的判断进行了补充:如何判断一个点位于线的左边还是右边
通过叉积,然后根据最后值的正负来判断点位线的哪一边
这边老师带过了一个算法:Graham Scan
想象你在给钉子围上橡皮筋:
从最左边的点开始;
你想找到 “下一个最外面的点”;
怎么找?转一圈,看哪个点相对现在这个点最靠外(最逆时针);
一直这样找下去,直到绕一圈回到起点。
# ch (凸包) 的三种构造算法
# Gift Wrapping(礼物包装)
通俗来理解可以说:
想象你在给钉子围上橡皮筋:
从最左边的点开始;
你想找到 “下一个最外面的点”;
怎么找?转一圈,看哪个点相对现在这个点最靠外(最逆时针);
一直这样找下去,直到绕一圈回到起点。
实际上的过程就是:
你从左下角开始;
你看着所有点,假装你有一个 “激光扫描器”;
每次 “顺着边扫一圈”,找到第一个 “最外边” 的点(就是逆时针最远的点);
把这条边加入凸包;
然后从这个新点再开始下一轮扫描。
你站在当前点 A,看到两个候选点 B 和 C:谁更逆时针?就是谁形成的角度更大;
用叉积公式判断:
- 如果 cross 值为正,说明 C 比 B 更逆时针 ⇒ C 更好!
所以,就是一个从起点开始,转着找下一个点 -> 再以下一个点为起点,找下一个点的过程,直到找到所有的点。
那么,有优化的空间嘛?
有的!
可以不用每次都遍历所有的点以找下一个点 q,而是可以通过检查角度来判断,如果新的角度大于旧的最大角,那么这个点就是新的点。
所以,他们的区别在于,暴力解法需要对所有的点做叉积比较来判断是否都在左边,而优化后只需要比较角度就可以了
一个是 的时间复杂度,因为需要嵌套;而后者为 O (n) 的复杂度,只需要线性即可。
# quick hall
Divide-and-Conquer approach 分治
这个算法可以理解成
想象你在玩 “谁离得最远” 的游戏:
找出最左点 AAA 和最右点 BBB,画出一条直线 ABABAB;
把所有点分成两边(上方、下方);
找出 “离直线最远” 的那个点 CCC,它肯定是边界上的;
把 C 连上 A 和 B,形成三角形;
再看哪些点在这个三角形 “外面”,对它们重复上面的步骤;
直到所有点都在边界三角形里为止,完工!
![[Pasted image 20250603145633.png]]
时间复杂度:
最好情况:每次都能把点均匀切开
即二分查找复杂度。O (nlogn)
最差情况:所有点都在边上,如五角星形的分布
# 扫描线算法
将这些点以 x 轴从小到大排列,并且分为下凸壳和上凸壳,然后最后再将他们拼起来。
在凸包上行走时观察是否右拐,如果左拐了就把这个点删了。如果右拐就继续。
伪代码:
1 | 1. Sort the points left to right |
因为每个点只会加入和删除一次,所以时间复杂度为 O (nlogn)
然后就是计算凸包的下界,我们可以低于 nlogn 吗?
用数学归纳法,将凸包转化为排序问题,而排序算法的下界为 nlogn,所以凸包不可能低于 nlogn
# Chen 算法
Chan 算法就像一个聪明的厨师:
Graham 是大锅炒(适合处理很多点);
Gift Wrapping 是慢工细活(适合边界点很少);
Chan 想了一个办法:先猜边界点有多少(比如 h=4),然后只挑这些来 Gift Wrapping,结果超快!
相当于是 graham 和 gift wrapping 的结合,将所有点分为 n/h 组,然后对每一组使用 graham 算法,得到每一组的局部凸包,然后对这些局部凸包使用 gift wrapping,所以最终的复杂度为 nlogh