凸包Graham Scan算法實現
凸包算法實現點集合中搜索凸包頂點的功能,可以處理共線情況,可以輸出共線點也可以不輸出而只輸出凸包頂點。經典的Graham Scan算法,點排序使用極角排序方式,并對共線情況做特殊處理。一般算法是將共線的點去掉距離小的,保留最遠的,這樣處理會導致不能輸出凸包邊上的點,只能輸出頂點。但是有時候需要輸出這些邊上的點,因此這里我將共線點都保留,并按照順序排列。共線點排列方式是:非起始邊按照從遠道近排列,起始邊按從近到遠排列。
實現如下:#include <iostream>
#include <math.h>
using namespace std;
typedef struct{double x,y;} Point;
void qsortpoint(Point s[],Point base,int start,int end);
void sortstartedge(Point s[],int nums);
//向量(x1,y1),(x2,y2)的叉積
double CrossMul(double x1,double y1,double x2,double y2)
{return x1*y2-x2*y1;
}
//向量(x1,y1),(x2,y2)的點積
double DotMul(double x1,double y1,double x2,double y2)
{return x1*x2+y1*y2;
}
//跨立判斷
//判斷點c是在向量ab的逆時針方向還是順時針方向,大于零逆時針,等于0則共線
double CrossMul(Point a,Point b,Point c)
{return CrossMul(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
//計算向量ab和ac點積
double DotMul(Point a,Point b,Point c)
{return DotMul(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
//判斷浮點數符號
int doublecmp(double d)
{if(fabs(d)<10e-6)return 0;return d>0?1:-1;
}
//判斷同一直線上的三個點位置,點c是否在點ab之間
bool betweenCmp(Point a,Point b,Point c)
{if(doublecmp(DotMul(c,a,b))<=0)return true;return false;
}
//判斷j是否在base->i向量的左邊或當共線時j是否位于它們的線段之間
bool isLeftorNearer(Point base,Point i,Point j)
{if(CrossMul(base,i,j)>0)return true;if(CrossMul(base,i,j)==0 && betweenCmp(base,i,j))return true;return false;
}
void swap(Point& a,Point& b)
{Point temp = b;b=a;a=temp;
}
//以s中的最低點為參考點,對其他所有點進行極角排序(逆時針)
//共線時離參考點較遠的點排在前面,凸包的起始邊共線點從近到遠排列
void sortpoint(Point s[],int nums)
{//找最低點for(int i=1;i<nums;i++){if(s[i].y<s[0].y || (s[i].y==s[0].y && s[i].x<s[0].x))swap(s[0],s[i]);}qsortpoint(s,s[0],1,nums);//將起始邊上的共線點重新排列sortstartedge(s,nums);
}
void sortstartedge(Point s[],int nums)
{int i,j;for(i=2;i<nums;i++){if(CrossMul(s[0],s[1],s[i])!=0)break;}for(j=1;j<(i+1)/2;j++)swap(s[j],s[i-j]);
}//將點按極角逆時針排序
void qsortpoint(Point s[],Point base,int start,int end)
{if(start>=end)return;Point partition = s[end-1];int i=start-1,j=start-1;while(++j<end-1){if(isLeftorNearer(base,s[j],partition)){swap(s[++i],s[j]);}}swap(s[++i],s[end-1]);qsortpoint(s,base,start,i);qsortpoint(s,base,i+1,end);
}
void ConvexHull(Point s[],int nums,Point result[],int& resultnums)
{sortpoint(s,nums);resultnums = 0;if(nums<=3){for(int i=0;i<nums;i++)result[resultnums++] = s[i];return;}int top=0;int i;for(i=0;i<2;i++)result[top++] = s[i];while(i<nums){//用<號判斷則包含凸包邊上的共線點,<=號判斷則只包含凸包頂點if(CrossMul(result[top-2],result[top-1],s[i])<=0){top--;}else{result[top++] = s[i++];}}//最后加入起點形成閉包while(CrossMul(result[top-2],result[top-1],s[0])<=0){top--;}result[top++]=s[0];resultnums = top;
}
int main()
{Point pa[] = {{0,0},{1,0},{2,0},{3,0},{4,0},{4,1},{4,2},{4,3},{4,4},{3,4},{2,4},{1,4},{0,4},{0,3},{0,2},{0,1},{2,2},{1,1}};cout<<"convex hull is:"<<endl;Point result[18];int nums;ConvexHull(pa,18,result,nums);for(int i=0;i<nums;i++)cout<<result[i].x <<"," <<result[i].y<<endl;return 0;
}
經驗證,算法無誤。
總結
以上是生活随笔為你收集整理的凸包Graham Scan算法实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。