凸包问题——Graham扫描法



查看原文:http://www.wyblog.cn/2016/12/01/%e5%87%b8%e5%8c%85%e9%97%ae%e9%a2%98-graham%e6%89%ab%e6%8f%8f%e6%b3%95/

首先明白什么是凸包?

  • 点集Q的凸包是指一个存在的最小凸多边形,满足Q中的所有点或者在多边形边上或者在其内。

求解凸包问题有几种方法,这里介绍最常用的Graham扫描法,并给出代码模板,最后再介绍几种其他求法。

Graham扫描法


算法步骤为:

  1. 首先,找到所有点中纵坐标最小的点$latex P_{0}$ ,如果y坐标相同,找x坐标最小的,则这个点一定在凸包上。
  2. 以这个$latex P_{0}$点为基准求所有点相对于它的夹角α,并按照极角对这些点排序,前述基准点在最前面,当α相同时,距$latex P_{0}$点近的在前边,这些点组成一个栈,设为P[0],P[1]...P[n-1]。 注:这样预处理后,保证p[0],p[1]和p[n-1]都是凸包上的点.
  3. 建立一个栈,初始时P[0]、P[1]、P[2]进栈,对于 P[3..n-1]的每个点,若栈顶的两个点组成的直线与它不构成"逆时针旋转"的关系,则将栈顶的点出栈,直至没有点需要出栈以后(即满足点在栈顶两元素组成直线的左侧这个条件时)再将当前点入栈。
  4. 所有点处理完之后栈中保存的点就是凸包了。

具体过程如图所示:

在上图b中,P3点在P1与P2点组成的直线的右侧,所以将P2出栈,P3入栈,其他步骤同理。

引用以下博客的Graham扫描算法代码:

http://www.cnblogs.com/xiaohongmao/archive/2013/12/10/3467460.html


const int INF=0xfffffff ; struct Point{ int x,y ; } ; Point p[50005],s[50005] ; int top ; int direction(Point p1,Point p2,Point p3) { return (p3.x-p1.x)*(p2.y-p1.y)-(p2.x-p1.x)*(p3.y-p1.y) ; } int dis(Point p1,Point p2) { return (p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y) ; } int cmp(Point p1,Point p2)//极角排序 { int temp=direction(p[0],p1,p2) ; if(temp<0)return 1 ; if(temp==0 && dis(p[0],p1)<dis(p[0],p2))return 1 ; return 0 ; } void Graham(int n) { int pos,minx,miny ; minx=miny=INF ; for(int i=0 ;i<n ;i++) if(p[i].x<minx || (p[i].x==minx && p[i].y<miny)) { minx=p[i].x ; miny=p[i].y ; pos=i ; } swap(p[0],p[pos]) ; sort(p+1,p+n,cmp) ; p[n]=p[0] ; s[0]=p[0] ;s[1]=p[1] ;s[2]=p[2] ; top=2 ; for(int i=3 ;i<=n ;i++) { while(direction(s[top-1],s[top],p[i])>=0 && top>=2)top-- ; s[++top]=p[i] ; } }

其它算法


穷举法,时间复杂度为$latex O(n^{3})$。 步骤为:

  1. 将点集里面的所有点两两配对,组成 n(n-1)/2 条直线。
  2. 对于每条直线,再检查剩余的 (n-2) 个点是否在直线的同一侧。

这里要判断方向的话,可以将三个点组成行列式,最右列补为1来计算。

分治法,时间复杂度为$latex O(nlogn)$。 步骤为:

  1. 把所有的点都放在二维坐标系里面。那么横坐标最小和最大的两个点 P1 和 Pn 一定是凸包上的点。直线 P1Pn 把点集分成了两部分,分别叫做上包和下包。
  2. 对上包:求距离直线 P1Pn 最远的点。
  3. 作直线 P1Pmax 、PnPmax,把直线 P1Pmax 左侧的点当成是上包,把直线 PnPmax 右侧的点也当成是上包。 重复步骤 2、3。 对下包也作类似操作。

Jarvis步进法,时间复杂度:O(nH),其中 n 是点的总个数,H 是凸包上的点的个数。 步骤为:

  1. 首先,纵坐标最小的那个点一定是凸包上的点。
  2. 从 P0 开始,按逆时针的方向,逐个找凸包上的点,每前进一步找到一个点,所以叫作步进法。
  3. 利用夹角寻找下一个点。假设现在已经找到 {P0,P1,P2} 了,要找下一个点:剩下的点分别和 P2 组成向量,设这个向量与向量P1P2的夹角为 β 。当 β 最小时就是所要求的下一个点了,此处为 P3 。注意,当夹角相同时,距离远的那个点是凸包上的点。

参考资料:

http://blog.csdn.net/bone_ace/article/details/46239187



查看原文:http://www.wyblog.cn/2016/12/01/%e5%87%b8%e5%8c%85%e9%97%ae%e9%a2%98-graham%e6%89%ab%e6%8f%8f%e6%b3%95/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值