1. 理论原理推导
核心思想
Graham 扫描法基于以下基本思想:
-
极角排序:
选取一个参考点(通常选择 y 坐标最小的点,若存在多个,则选 x 坐标最小的),将其他点按照与该参考点构成的极角进行升序排序。这样排序后的点序列沿参考点方向依次展开,便于后续的凸包构造。 -
利用栈进行构造:
从参考点开始,依次将排序后的点加入栈中。每加入一个新点,都检查当前栈顶的两个点与新点形成的转向:-
如果转向为左转(或者说逆时针转),说明当前点有可能是凸包的一部分,继续压入栈中;
-
如果转向为右转(顺时针)或共线,说明栈顶点不满足凸包条件,需要弹出栈顶点。如此反复,直至满足左转条件,再将当前点压入栈中。
-
几何证明
-
正确性证明:
利用“循环不变量”思想,可以证明:-
在每一步,栈中保存的点构成部分凸包(即所有点均处于边界的外侧)。
-
如果出现右转,则说明栈顶点处于内部,不可能在最终凸包上,将其删除。
由此,通过反复修正转向,最终栈中剩下的点必然构成整个点集的凸包。
-
-
关键性质:
对于任何三个连续的点,
和
,如果构成右转,则
一定不在凸包边界上;如果总是保持左转,则整个路径是凸的。
这一性质正是构造凸包的理论依据。
2. 时间复杂度推导
Graham 扫描法主要分为两个阶段:
-
极角排序阶段:
-
对 n 个点按照极角排序,通常采用快速排序或归并排序,其时间复杂度为 O(nlogn)。
-
-
扫描与构造阶段:
-
利用栈对排序后的点进行扫描,判断每次转向是否为左转或右转。每个点最多入栈一次、出栈一次,故该阶段的时间复杂度为 O(n)。
-
综合时间复杂度:
主要耗时在排序阶段,故总体时间复杂度为O(nlogn)
3. 算法步骤
步骤 1:选择参考点
-
在所有点中选择 y 坐标最小的点作为参考点(若 y 坐标相同,则选择 x 坐标最小的点),记为
。
步骤 2:极角排序
-
以
为原点,计算所有其他点与
之间的极角。
-
对所有点(除了
)按照极角升序排序。
-
注意:在极角相同的情况下,通常保留离
最近的点,或者按距离排序,确保排序结果有利于凸包构造。
-
步骤 3:初始化栈
-
将
以及排序后的前两个点依次压入栈中。
步骤 4:扫描构造凸包
-
对于排序后的其余每个点
(从第 3 个点开始):
-
检查栈顶的两个点(记为
和
)与
构成的转向。
-
如果转向为右转(或共线不满足要求),则说明
不在凸包上,从栈中弹出该点。
-
重复上述检查,直到栈顶的两个点与
构成左转为止。
-
将
压入栈中。
-
步骤 5:输出结果
-
当所有点处理完毕后,栈中保存的点按顺序构成整个点集的凸包。
算法总结
-
理论原理:
通过选择参考点、极角排序以及利用栈检查转向,保证了每一次只保留凸包边界上的点。利用几何性质证明了只有左转时点才属于凸包,右转时弹出不符合条件的点。 -
时间复杂度:
排序阶段 O(nlogn),扫描阶段 O(n) ,整体为 O(nlogn) 。 -
算法步骤:
包含选择参考点、极角排序、栈初始化、扫描构造凸包以及输出结果等步骤,流程清晰且易于实现。