博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【转】Graham求凸包
阅读量:5732 次
发布时间:2019-06-18

本文共 2284 字,大约阅读时间需要 7 分钟。

作者自转,原文链接:

正文

  网上已经有很多关于Graham-scan的资料了。

  Graham扫描法的时间复杂度为O(nlogn),是通过维持一个关于候选点的栈来解决凸包问题。输入的每个点都被压入栈一次,其中不在凸包上的点被弹出。当算法终止时,栈中仅包含凸包中的点,并且从栈底到栈顶按逆时针顺序排列。(摘自算法导论)
  首先要对输入的点进行排序。排序有两种,一种是极角序,一种是水平序。极角序容易理解但是不容易实现,水平序容易实现但是不容易理解。排序调用algorithm里的sort()函数即可,关键的是写好cmp函数。
  首先说极角序。选择最“左下”的点为基点,即选择纵坐标最小的点,有多个时选择其中横坐标最小的点,因为这个点一定在凸包上。设基点为K,然后对比排序剩下的点A、B,向量KA和向量KB与x轴的角(以逆时针为正)的大小。排序完后,建立一个栈,将基点与前两个点压入,然后扫描到第n个点结束。由于凸包一定是凸多边形,所以比较方式就是,取当前点X,栈顶点Y,次栈顶点Z,假设新加入的点在凸包上,那么需要考虑栈顶点是否也在凸包上,如果在,那么向量YX一定在向量ZY的逆时针方向,使用向量叉积的正负就可以判断。
  然后说水平序。水平序直接按坐标排序即可,实现非常方便。在扫描的时候和极角序方法一样。但是水平序需要注意右链和左链问题。因为水平序的排序方式,第一个点一定在最下方,第n个点一定在最上方,这样从1扫描到n的时候由于扫描顺序的问题,只有右边在凸包上的点被保留,所以是完整凸包被1、n两点的线段分开后的右半部分(自己模拟一下便可理解),所以需要再扫描左链。。然后从n到1扫描左链即可。值得注意的是,右链扫描完后,栈顶元素就是n,所以开始时为了避免重复只将n-1点压入栈,从n-2循环到1.

极角序:

#include
  #include
  #include
  #include
  #include
  #include
  #include
  #define pow2(a) a*a  #define max(a,b) ((a>b)? a:b)  using namespace std;  long n;  struct dian  { char a;   long x,y;} d[1000];  stack
 zhan;  dian dd;    long chaji(dian a,dian b,dian c)  { return ((a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y));}    double juli(dian a,dian b)  { return sqrt(pow2(a.x-b.x)+pow2(a.y-b.y));}    bool cmp(dian a,dian b)  { long s=chaji(a,b,d[0]);   if(s>0||((s==0)&&(juli(d[0],a)
=0)            {             zhan.push(c);//包括共线点,若为>,则是不包括共线点。                  break;            }        }          zhan.push(i);    }}    int main()  { cin>>n;   long h;   for(long i=0;i
>d[i].a>>d[i].x>>d[i].y;          if(i==0)        {         dd=d[i];              h=0;              continue;        }          else        {         if(d[i].x==dd.x)         {         if(d[i].y

水平序:

#include
  #include
  #include
  #include
  #include
  #include
  #define max(a,b) ((a>b)? a:b)  using namespace std;  long n,m;  struct dian  { long x,y;    char a;} d[100050];  stack
 zhan;  dian p1,p2;    bool cmp(dian a,dian b)  { if(a.y==b.y) return a.x
=1;i--) { while(1) { if(zhan.size()==w) break;              a=zhan.top();              zhan.pop();              b=zhan.top();              p1.x=d[a].x-d[b].x;              p1.y=d[a].y-d[b].y;              p2.x=d[i].x-d[a].x;              p2.y=d[i].y-d[a].y;              if(chaji(p1,p2)<=0)            {             zhan.push(a);//包括共线点,若为“<”,则不包括                   break;            }        }          zhan.push(i);    }}    int main()  { cin>>n;    for(long i=1;i<=n;i++) cin>>d[i].a>>d[i].x>>d[i].y;   graham();   while(!zhan.empty()) { cout<
<

最后关于共线点,一般是不包括的,这样可以减少凸包里点的个数,也算一个小优化。

转载地址:http://nfmwx.baihongyu.com/

你可能感兴趣的文章
用户和开发者不满苹果iCloud问题多多
查看>>
Windows 8上安装本地回环网卡
查看>>
一位多年老站长告白:如何用老域名让新站快速上首页
查看>>
iOS开发那些事-Passbook详解与开发案例(附视频)
查看>>
想生男想生女 从科学角度为你解读
查看>>
[转]Best way to sort a DropDownList in MVC3 / Razor using helper method
查看>>
implements Serializable有什么作用
查看>>
WebService 入门程序(一)
查看>>
经典网页设计:滚动技术应用得出神入化的18个网站
查看>>
poj - 1873 - The Fortified Forest
查看>>
Android--通知之Notification
查看>>
[转载]WCF系列_分布式事务(下)
查看>>
远程电脑备份与还原数据库
查看>>
Unity中小地图做法
查看>>
Flash AS 响应双击事件MouseEvent.DOUBLE_CLICK
查看>>
jQ函数after、append、appendTo的区别
查看>>
硬件负载均衡设备介绍
查看>>
linux如何查看CPU,内存,机器型号,网卡信息
查看>>
Bogart gData.vb
查看>>
Linux 学习手记(4):Linux系统常用Shell命令
查看>>