做者自转,原文连接:http://blog.csdn.net/nmlh7448...ios
网上已经有不少关于Graham-scan的资料了。
Graham扫描法的时间复杂度为O(nlogn),是经过维持一个关于候选点的栈来解决凸包问题。输入的每一个点都被压入栈一次,其中不在凸包上的点被弹出。当算法终止时,栈中仅包含凸包中的点,而且从栈底到栈顶按逆时针顺序排列。(摘自算法导论)
首先要对输入的点进行排序。排序有两种,一种是极角序,一种是水平序。极角序容易理解可是不容易实现,水平序容易实现可是不容易理解。排序调用algorithm里的sort()函数便可,关键的是写好cmp函数。
首先说极角序。选择最“左下”的点为基点,即选择纵坐标最小的点,有多个时选择其中横坐标最小的点,由于这个点必定在凸包上。设基点为K,而后对比排序剩下的点A、B,向量KA和向量KB与x轴的角(以逆时针为正)的大小。排序完后,创建一个栈,将基点与前两个点压入,而后扫描到第n个点结束。因为凸包必定是凸多边形,因此比较方式就是,取当前点X,栈顶点Y,次栈顶点Z,假设新加入的点在凸包上,那么须要考虑栈顶点是否也在凸包上,若是在,那么向量YX必定在向量ZY的逆时针方向,使用向量叉积的正负就能够判断。
而后说水平序。水平序直接按坐标排序便可,实现很是方便。在扫描的时候和极角序方法同样。可是水平序须要注意右链和左链问题。由于水平序的排序方式,第一个点必定在最下方,第n个点必定在最上方,这样从1扫描到n的时候因为扫描顺序的问题,只有右边在凸包上的点被保留,因此是完整凸包被一、n两点的线段分开后的右半部分(本身模拟一下即可理解),因此须要再扫描左链。。而后从n到1扫描左链便可。值得注意的是,右链扫描完后,栈顶元素就是n,因此开始时为了不重复只将n-1点压入栈,从n-2循环到1.c++
#include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<stack> #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<long> 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)<juli(d[0],b)))) return true; else return false; } void graham() { sort(d+1,d+n,cmp); zhan.push(0); zhan.push(1); zhan.push(2); dian a,b; long c; for(long i=3;i<n;i++) { while(1) { a=d[zhan.top()]; c=zhan.top(); zhan.pop(); b=d[zhan.top()]; if(chaji(d[i],b,a)>=0) { zhan.push(c);//包括共线点,若为>,则是不包括共线点。 break; } } zhan.push(i); } } int main() { cin>>n; long h; for(long i=0;i<n;i++) { cin>>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<dd.y) { dd=d[i]; h=i; continue; } } if(d[i].x<dd.x) { dd=d[i]; h=i; } } } dd=d[h]; d[h]=d[0]; d[0]=dd; graham(); while(!zhan.empty()) { cout<<d[zhan.top()].a<<' '; zhan.pop(); } return 0; }
#include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<cmath> #include<stack> #define max(a,b) ((a>b)? a:b) using namespace std; long n,m; struct dian { long x,y; char a; } d[100050]; stack<long> zhan; dian p1,p2; bool cmp(dian a,dian b) { if(a.y==b.y) return a.x<b.x; return a.y<b.y; } long chaji(dian a,dian b) { return (a.x*b.y-b.x*a.y); } void graham() { sort(d+1,d+1+n,cmp); zhan.push(1); zhan.push(2); long a,b; for(long i=3;i<=n;i++) { while(1) { if(zhan.size()<2) 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); } long w=zhan.size(); zhan.push(n-1); for(long i=n-2;i>=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<<d[zhan.top()].a<<endl; zhan.pop(); } return 0; }
最后关于共线点,通常是不包括的,这样能够减小凸包里点的个数,也算一个小优化。算法