申明,本文非笔者原创,原文转载自:http://blog.csdn.net/tmljs1988/article/details/7259331
凸包问题 —— Graham扫描法:
(1)找出点集p[]中最左下的点p1,把p1同点集中其他各点用线段连接,并计算这些线段与水平线的夹角,然后按夹角从小到大和按到p1的距离从近到远排序(夹角范围为 [0, 180)度,而且可以删除相同夹角且距离p1较近的点,保留最远点,这样可减少计算量。因为直线上的非端点不是凸包的极点,即如果p1,p2,p3在一条直线上,则只取凸点p1,p3。p2不在端点,故可以去掉),得到新的节点序列p1,p2,...pn.依次连接这些点,得到一个多边形(已经逆时针,有所进展,但还需去掉不在凸包上的点)。此时p1是凸包的边界起点,p2和pn也是最终凸包的顶点,p[n+1]=p1(看成循环的)
(2)删除p3,p4,...p[n-1]中不在凸包上的点:
先把p1,p2,p3入栈S中,再依次循环(i = 3 -> n-1),若栈顶的两个点和当前的点p[i]这三点连线的方向向顺时针方向偏转,表明是凹的,应删除,则栈顶元素出栈(要循环判断,即可能前面的仍是凹的,还需再出栈,举例如下图),直到向逆时针方向偏转或者栈内只有2个元素了(p1p2),就把当前点p[i]入栈。
最后栈中的元素就是最终凸包上的点。
分析:一般会从最左下点p1开始,根据所有点斜率中最小的求下一个凸包点pa,再根据pa的所有点斜率中最小的来求下一个凸包点pb,依此类推,但这样就是三重循环(p1,pa,pb是一次,p1,pa,pb内部的排序有2次,共三重循环),这样效率不高(其实这就是卷包裹法,复杂度为O(NH),其中N是全部的点数 H是最终在凸包上的点数)。Graham扫描法只取所有点对p1的斜率,后面的点充分利用该斜率的信息,并作某些处理,进行改进,以提高效率。这是由多到少,由多个到1个的方法,并充分利用已知条件。
下面图例也可说明栈扫描的过程(摘自http://www.cnblogs.com/Booble/archive/2011/03/10/1980089.html#2065991):
----------------
---------------
--------------------
以下截图代码摘自《ACM程序设计培训教程 吴昊 中国铁道出版社》 :