问题:输入平面上的n个点的集合Q,需输出CH(Q),即Q的凸包。
Graham-Scan算法:
当我们沿着凸包逆时针漫游时,在拐角处总是向左转。因此我们assume所有点在极坐标系下按照极角大小排列,我们逆时针去遍历所有点,去除非凸包的顶点(也就是非左转点)
p0,p1和pn一定在最后生成的凸包中。先入栈3个(3个顶点一定可以生成一个凸包);
如果next-to-top元素、top元素与新元素形成了非左移动(右拐了),就出栈top元素;一直出栈top元素直到next-to-top元素、top元素与新元素形成左移动。
将pi点进栈并判断下一个点,直到点pn判断完。
时间复杂性为O(nlogn):
S1是Selection问题,O(n)解决;S2普通排序,需要O(nlogn);S3O(1);S4-7:貌似最坏的时间是外层O(n),内层也O(n),总体上是O(n²),但事实上每个元素至多进栈一次、出栈一次。所以总体上这三步耗费的时间最多为O(n)。(可以想象一下最坏情况,判定到pn的时候需要把前面n-2个点全部出栈,这也意味着p4-p(n-1)加入时都不需要出栈)
算法正确性分析:
往证CH(Qj∪{pi})=CH(Qi):
上图倒数第二行写错了,实际上是:
注意:pj是在while循环结束后设的栈顶元素。也就是说for循环的j+1次迭代到第i次迭代向外弹出点这个动作结束之后留下的符合左移动的点,Qi减去pk的加和后正好就是Qj加点pi。
如何使用分治思想对Graham算法进行改进?
如何Merge?
获得3段相对于p点来说极角逆时针增大的序列。有了相对的大小位置再进行合并的话只需要O(n)的时间复杂度。比如先扫一遍获得最大的有序序列<g,h,i,j,k>花θ(n),将,其余序列逐个插入。如<a,b,c,d>,a如果在g,h后面那bcd也不需要和gh做比较了。最多也只比较了O(2n)而已。
比起不分治的Graham算法,时间复杂度也没有优到哪儿去。个人觉得是因为两个凸包并没有为合成一个大凸包(Merge阶段)提供的帮助不够大,也就是提供有序序列还有不需要遍历所有点就能生成大凸包。