Graham算法

原文地址

一、凸包定义

通俗的说就是:一组平面上的点,求一个包含所有点的最小凸多边形,这个最小凸多边形就是凸包。

二、Graham算法思想

概要:Graham算法的主要思想就是,最终形成的凸包,即包围所有点的凸多边形,假定多边形是按逆时针方向生成的,那么多边形内部包围的所有点与多边形每个有向边的关系都是:点在有向边的左边。依照此思想,只要找到一个出发点,然后依此出发点按逆时针方向构建多边形,并保证每加入一条有向边时,都要满足其余点都在该边的左边。

 

***点与线的关系
定义:平面上的三点P1(x1,y1),P2(x2,y2),P3(x3,y3)的面积量:
|x1 x2 x3|
S(P1,P2,P3) = |y1 y2 y3| = (x1-x3)*(y2-y3) – (y1-y3)*(x2-x3)
|1 1 1 |
当P1P2P3逆时针时S为正的,当P1P2P3顺时针时S为负的。

令矢量的起点为A,终点为B,判断的点为C,
如果S(A,B,C)为正数,则C在矢量AB的左侧;
如果S(A,B,C)为负数,则C在矢量AB的右侧;
如果S(A,B,C)为0,则C在直线AB上。

具体算法过程:

(1)输入:点集S={P}

(2)寻找基点P0:在所有点中找到y坐标最小的点,若找到多个,则选取其中X坐标最大的点作为基点,若只找到一个,则直接以这个点作为基点。

(3)排序:以基点为起点,以其余点为终点构成一个向量<P0,PK>,逐个计算每个向量与x轴正方向的夹角,并按夹角有小到大进行排序,得到一个排序的点S1={p0,p1,p2,p3…p(N-1)};对于夹角相同的点,剔除掉离基点近的点,只保留离基点最远的那个点。

注意:由于计算角度复杂且耗时,在这里采用另外一种方式处理,根据上面的点线关系,从基点p0出发,依次遍历其它点,设为pk,p0和pk就构成一条有向向量,依次判断其它点(如pm)在向量的哪个方向,若在线段右边,则用其它点代替pk,构成一个新向量p0pm,继续判断剩余的点,这样一趟下来,就能找到最右边的点;依此道理判断其他点。如图:从向量p0p3(p3是任意选的,最终要将除p0外的所有点选到即可)开始,p1在向量p0p3左边,不变;p2在p0p3左边,向量不变;p4在p0p3右边,这时要将比较的向量变为p0p4;继续遍历p5,p5在p0p4右边,向量变为p0p5;继续遍历p6,p6在向量p0p5右边,向量变为p0p6;遍历p7,p7在向量p0p6右边,向量变为p0p7,这一趟下来就将p7这一个最右边的点找到了。同样的方法排序其他点,最后向量按与x轴正方向的顺序就是{p7,p6,p5,p4,p3,p2,p1},依次递增。

 

(4)构造凸包:

第一步:首先将基点p0入栈,p1和p2也依次入栈;

第二步:取栈顶的前两个点构成向量,即向量<p(k-1),pk>;

第三步:判断点p(k+1)是否在向量的左边;

第四步:

    情况1:若在向量的左边,则将点p(k+1)入栈,重复第二步;

    情况2:若在向量的右边,将点pk出栈,继续取下一个点,重复步骤二。

第五步:最后栈中存储的点就为凸包。

 

三、编程实现

1、判断点p3是否在p1p2左边函数。(注意计算机屏幕坐标系与数学直角坐标系,y轴方向相反)

 1 int CMyMath::Isleft(Cpoint2D p1,Cpoint2D p2,Cpoint2D p3)
 2 {
 3     int s;
 4     s = (p1.x-p3.x)*(p2.y-p3.y)-(p1.y-p3.y)*(p2.x-p3.x);
 5     if (s<0)
 6     {
 7         return 1; //点在直线左侧(对屏幕坐标系)
 8     }
 9     else if (s>0)
10     {
11         return 2;//点在直线右侧
12     }
13     else
14     {
15         return 0;//点在直线上
16     }        
17 }

2、定义一个点类

 1 class Cpoint2D  
 2 {
 3 public:
 4     Cpoint2D();
 5     Cpoint2D(int x,int y);
 6     virtual ~Cpoint2D();
 7 public:
 8     int id;
 9     int x,y;
10 };

3、定义一个CGramhamCaclu类,用来生成凸包

 1 class CGramhamCaclu  
 2 {
 3 public:
 4     CGramhamCaclu();
 5     CGramhamCaclu(CGramhamCaclu& crah);
 6     virtual ~CGramhamCaclu();
 7 public:
 8     CArray<Cpoint2D,Cpoint2D> InitialPoints;//保存最初点的集合
 9     CArray<Cpoint2D,Cpoint2D> SortPoints;  //保存排序后的点的集合
10     CArray<Cpoint2D,Cpoint2D> temparr;//用来起栈的作用,保存凸包中的点
11     int top1,top2;//栈顶前两项索引
12     int index;    //基点的索引
13 public:
14     void DrawPoints(CDC* pDC);//画原始点
15     void DrawMinmumPolygon(CDC*pDC);//画凸包
16     void CaculTuBao();//计算凸包
17 protected:
18     void InitialSortPoints();
19     int FindBasePoint(CArray<Cpoint2D,Cpoint2D>& cp);//找基点
20     void Exchange(int index1,int index2);
21     bool Sort();//排序
22     void Stack();//构造凸包
23 };

4、CGramhamCaclu类详细代码

View Code

5、测试结果图

参考资料:http://geomalgorithms.com/a10-_hull-1.html

说明:以上内容都是个人理解,如有错误或不足,欢迎指正!

发表评论

邮箱地址不会被公开。 必填项已用*标注