【轉】Graham求凸包

做者自轉,原文連接: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;
}  

最後關於共線點,通常是不包括的,這樣能夠減小凸包裏點的個數,也算一個小優化。算法

相關文章
相關標籤/搜索