计算几何-凸包-Graham算法

一、点集有序化-水平排序

  在计算几何中,点集往往无序,因此在计算前需要对点集进行排序,使得算法可以有序高效运行。

水平排序利用点在二维平面上固有的横纵坐标属性进行排序,只涉及点坐标的比较,与极坐标排序使用的三角函数相比没有精度问题。

水平排序利用的规则为,纵坐标为第一关键字,横坐标为第二关键字,纵坐标升序排列,如果纵坐标相等,按横坐标升序排列。

1 bool com(const datatype& x,const datatype& y){
2     if(x.y<y.y)
3         return true;
4     if(x.y>y.y)
5         return false;
6     if(x.y==y.y)
7         return x.x<y.x;
8 }

二、Graham计算凸包

  点集有序化后,Graham按逆时针顺序遍历点集,利用栈不断更新迭代凸包结果。

  遍历的顺序是,在水平排序后将会出现一个最低点和一个最高点。将最低点加入队列,先从最低点出发遍历,如果在最低点和最高点连线的右侧,就将该点加入访问队列,否则不加入。将最高点加入队列,再从最高点出发,如果在连线左侧,就将该点加入队列。最后形成的顺序序列长度应当为n+1,因为要回到最低点,所以最后要补一个最低点,并对n做调整。

 1 int cal(int x1,int x2,int y1,int y2,datatype p){
 2     return (x2-x1)*p.y-y1*(x2-x1)-(y2-y1)*p.x+x1*(y2-y1);
 3 }
 4 void geometry_sort(int n){
 5     sort(point+1,point+n+1,com);
 6     a[1]=point[1];
 7     int m=1;
 8     int x1=point[1].x;
 9     int x2=point[n].x;
10     int y1=point[1].y;
11     int y2=point[n].y;
12     for(int i=2;i<n;i++)
13         if(cal(x1,x2,y1,y2,point[i])<=0)
14             a[++m]=point[i];
15     a[++m]=point[n];
16     for(int i=n-1;i>=1;i--)
17         if(cal(x1,x2,y1,y2,point[i])>=0)
18             a[++m]=point[i];
19     return;
20 }

  确定遍历顺序后,按顺序枚举点,同时使用一个栈来记录结果。每次枚举到一个新点后,如果栈内存有不到2个点,就直接入栈,不考虑叉积,如果栈内存有2个以上点,就将新点和栈顶的两个点进行计算,确定路线叉积,如果叉积小于或等于0,说明无法构成凸多边形,就将栈顶点出栈(注意,此时新点还没有入栈),让新点继续和剩下的栈测试,直到不足2个点或者叉积大于0.

 

 1 datatype st[101];
 2 int top;
 3 int cross_product(datatype v1,datatype v2){
 4     return v1.x*v2.y-v1.y*v2.x;
 5 }
 6 bool check(datatype tmp){
 7     datatype x1=st[top-1];
 8     datatype x2=st[top];
 9     datatype x3=tmp;
10     datatype v1=datatype(x2.x-x1.x,x2.y-x1.y);
11     datatype v2=datatype(x3.x-x2.x,x3.y-x2.y);
12     return cross_product(v1,v2)>0;
13 }
1 geometry_sort(n);
2 n+=1;
3 top=0;
4 for(int i=1;i<=n;i++){
5     while(top>=2&&!check(a[i]))
6         top-=1;
7     st[++top]=a[i];
8 }

最后得到的栈,从栈底到栈顶的序列就是凸包的逆时针序列。

猜你喜欢

转载自www.cnblogs.com/shao0099876/p/9426299.html
今日推荐