1、问题定义:
(1)输入:平面上的n个点的集合Q ;输出: CH(Q),即Q的凸包
(2)Q的凸包:是一个最小凸多边形 P,Q的点在P上或者在P内
(3)凸多边形P: 连接P内任意两点的边都在P内
2、基本思想:
(1)当沿着凸包逆时针漫游时,总是向左转;
(2)在极坐标系下按照极角大小排列,然后按逆时针方向漫游点集,去除非凸包顶点(非左转点)
3、伪代码:
4、时间复杂性分析:T(n)=O(nlogn)
5、正确性分析:
【定理】设n个二维点的集合Q是Graham-Scan算法的输入,|Q|>=3,则算法结束时,栈S中自底到顶存储CH(Q)的顶点(按照逆时针顺序)
【证明】使用循环不变量方法
【循环不变量】在处理第i个顶点之前, 栈S中自底到顶存储CH(Qi-1)的顶点
【数学归纳法】
处理i=3之前,栈S中包含了Qi-1=Q2={p0,p1,p2 }中的顶点,这三个点形成了一个CH。循环不变量为真。
设在处理第i(i>=3)个顶点之前, 循环不变量为真,即栈S 中自底到顶存储CH(Qi-1)的顶点。
往证: 算法执行5~7步之后,栈S中自底到顶存储CH(Qi)的顶点。
(1)5~6步while循环执行结束后,第7步将pi压入栈之前,设栈顶元素为pj,次栈顶元素为pk,则此时,栈中包含了与for循环的第j轮迭代后相同的顶点,即CH(Qj),循环不变量为真。
(2)执行第7步之后, pi入栈, 则栈S中包含了CH(Qj∪{pi})中的顶点,且这些点仍按逆时针顺序,自底向上出现在栈中。(此时考虑是否CH(Qj∪{pi })=CH(Qi)?)
(3)对于任意一个在第i轮迭代中被弹出的栈顶点pt,设 pr为紧靠pt的次栈顶点,pt被弹出当且仅当pr 、pt 、pi构成非左移动。因此, pt不是CH(Qi ) 的一个顶点,即CH(Qi−{pt })= CH(Qi )。
(4)设Pi为for循环第i轮迭代中被弹出的所有点的集合,则有CH(Qi−Pi )= CH(Qi )
(5)又 Qi−Pi= Qj∪{pi },故有CH(Qj∪{pi })= CH(Qi−Pi )= CH(Qi )
(6)即得到:一旦将pi压入栈后, 栈S中恰包含CH(Qi )中的顶点, 且按照逆时针顺序,自底向上排列。
i=n+1,栈S中自底到顶存储CH(Qn )的顶点,算法正确。
证明完毕。
6、分治算法Divide-and-Conquer:
【边界条件】(时间复杂性为O(1))
(1)如果|Q|<3, 算法停止;
(2)如果|Q|=3, 按照逆时针方向输出CH(Q)的顶点;
【Divide】(使用O(n)算法求中值)
选择一个垂直于x-轴的直线把Q划分为基本相等的两个集合QL和QR,QL在QR的左边;
【Conquer】(时间复杂性为2T(n/2))
递归地为QL和QR构造CH(QL)和CH(QR);
【Merge】(时间复杂性为O(n))
(1)找到QL和QR中y坐标最小的点p (假设在QL中);
(2)在CH(QR)中找与p的极角最大和最小顶点u和v;
(3)构造如下三个点序列:a. 按逆时针方向排列的CH(QL)的所有顶点;b. 按逆时针方向排列的CH(QR)从v到u的顶点;c. 按顺时针方向排列的CH(QR )从v到u的顶点;
(4)合并上述三个序列;
(5)在合并的序列上应用Graham-Scan。
7、时间复杂性:
T(n)=2T(n/2)+O(n)
使用Master定理可知,T(n)=O(nlogn)