前注
以前CRAZY大神曾给我们讲过,但那时候我好像睡了……
第一次把凸包弄懂了噗哈哈哈
code太久的原因主要是度娘上面的同一种求凸包方法都不太一样太坑爹了
所以,我在这里写一些我自己对凸包的看法和理解,不喜勿喷
好了进入正题
凸包是个啥
凸包也算是用的比较多的了,那么什么是凸包?
给出平面内的一些点,过某些点画出一个凸多边形,且它要把所有点“包围”在里面,我们就叫它凸包
下面这个就是凸包
是不是豁然开朗了?
实在不懂,到这篇文章最下面翻一翻博客链接,应该就会了
graham算法求凸包
好那么问题来了,怎么求凸包?
在这里,我们使用比普通卷包裹法高效且更易懂的garham算法
请务必仔仔细细阅读以下文段
step one
首先,把所有的点平移一下,y坐标最小的点放到原点处
(为什么要平移?因为最底下的点一定是凸包上的点,证明略因为不需要证明)
平移后只需处理一二象限即可,为接下来的极角排序做好基础
step two
然后把所有点按极角(极角:所有点和原点的连线与x轴正半轴的夹角)为关键字升序快排
若极角相等的话则距离原点近的点的放在前面
(如何求极角?第一象限内:arctan(y/x)*180°/π;第二象限内就加180°)
那么排完序应该是这样的:
step three
接下来,我们使用一种入门数据结构——栈, O(n) O ( n ) 时间复杂度求出凸包:
①我们把p0和p1放在栈里面(因为p1也一定在凸包上),用一个循环从第三个点开始枚举当前点
②连接栈顶的两个点,得到直线 l
③如果当前点在l的左边或在l上,就把当前点入栈,回到步骤2
如果当前点在l的右边,那么栈顶的点不是凸包上的点,把栈顶的点出栈
(此时重新连接栈顶的两个点得到新的l)
④ 直到当前点在l的左边或在l上时,才把当前点入栈,回到步骤2
我来说明一下,还是上面的图:
p2在p0—p1的连线左边,p2入栈
p3在p1—p2的连线右边,p2出栈;p3在p0—p1的连线左边,p3入栈
p4在p1—p3的连线左边,p4入栈;p5在p3—p4的连线左边,p5入栈;p6在p4—p5的连线右边,p5出栈;p6在p3—p4的连线右边,p4出栈;p6在p1—p3的连线左边,p6入栈
p7在p3—p6的连线左边,p7入栈
p8在p6—p7的连线左边,p8入栈
最后栈内的元素为 (p0,p1,p3,p6,p7,p8) ( p 0 , p 1 , p 3 , p 6 , p 7 , p 8 ) ,它们就是凸包上的点
我已经用比较简单的说法说明了凸包的工作原理:
因为由于我们时时刻刻都保证栈内是一个凸的多边形 所以最后扫描完毕 栈里的所有点就是凸包
attention
注意两点
第一点
判断c是否在直线ab的两侧( a(x1,y1),b(x2,y2),c(x3,y3) a ( x 1 , y 1 ) , b ( x 2 , y 2 ) , c ( x 3 , y 3 ) )
若上式为正,在左侧,为负在右侧,0则在直线上
第二点
为什么栈顶元素出栈时要用while来做?
因为有可能整一连串的点都不在凸包上,我们要全部删去,下面的gif很清楚的说明了
再贴张动态解决凸包的gif图
gif中,p3→p4→p5→p6的过程就是连续出栈
小结
graham算法是解决凸包的一道利器
由于快排
O(nlog2n)
O
(
n
l
o
g
2
n
)
,求凸包
O(n)
O
(
n
)
,合起来就只是
O(nlog2n)
O
(
n
l
o
g
2
n
)
了,时间上来说非常优秀
会了graham,我们就能解决大部分的凸包问题啦!
又涨了不少姿势
后注
本文部分参考于
http://blog.csdn.net/bone_ace/article/details/46239187
http://www.cnblogs.com/Booble/archive/2011/03/10/1980089.html#2065991