graham演算法的思路,大概如下:對平面上的點的集合,從中找到有最小的y座標值的點p,然後根據其它點和p的連線與正x軸所成的角度將平面上的點進行排序,排序後,掃瞄從p開始的有序列表,如果所有的這些點都在凸包上,那麼每三個相繼的點,會組成乙個左旋,從另一方面說,如果相繼的三個點,p1,p2,p3,組成了乙個右旋,則可以立即去除p2,因為它不可能在凸包上!如此掃瞄到p3等於p時,掃瞄就結束,剩下的點,就全都是凸包上的點了!
這裡面有幾個難點:
1、如何根據他們的夾角,來對雙鏈表進行排序
2、如何判斷左右旋
3、如何掃瞄
針對第乙個問題,在下面的程式中,採用的是簡單的選擇排序,即每次在無序區內找到最大的元素,將其放到有序區內,因為這是對雙鏈表進行排序,就有雙鏈表的排序特點,但我認為採用插入,即斷開結點之間的聯絡始終不是好的辦法,所以採用了這種簡單的交換的方法,雖然效率慢了點,但是重在說明問題!
針對第二個問題,判斷左右旋,其實可以用乙個行列式的值來判定
|x1 x2 x3|
|y2 y2 y3|
|1 1 1 |
若該行列式為正,則為左旋,反之,則為右旋。其實這個行列式是根據點是在直線的上方還是下方而推斷出來的,用直線方程也可以判斷左右旋。
針對第三個問題看下面的**中的scan()函式就很清楚了!
下面是用c++寫的具體**:
#include #include using namespace std;
struct point
;typedef struct point type;
/*建立乙個雙向鍊錶*/
void creatlinklist_du(type *l,int n)
}void calculatecos(type *l)//找到y座標最小的元素,讓它是第乙個點,並計算每個點的cos值
p=p->next;
} float x0=pmin->x;
float y0=pmin->y;
p=l;
while(p) }
/*簡單選擇排序*/
void ******slectsort(type *l)//按cos值進行降序排序
if(p!=pmax)//將此最大值放進有序區,這裡只需要交換兩個結點的內容就可以了
}}float area(float x1,float y1,float x2,float y2,float x,float y)//計算三個點的左旋還是右旋
void scan(type *l)//順序掃瞄雙鏈表,判斷相繼的三個點是否組成左旋或是右旋
}while(!(p3==p && temp>=0.0));
} /*輸出雙向鍊錶中的元素*/
void outputlinklist_du(type *l)}
int main()
Graham凸包演算法簡介
凸包真是乙個神奇的演算法。對於平面上的一些點,我們要求凸包上所有的點,可以使用graham演算法 時間複雜度o nlogn 先找到最左下的點,把其他的點按叉積排序。然後維護乙個堆疊,每次利用叉積和棧頂比較判斷當前列舉到的點是否是凸包上的點,是則彈出棧頂元素 具體演算法click here 常熟巨大的...
graham求凸包演算法
問題 點集q的凸包 convex hull 是指乙個最小凸多邊形,滿足q中的點或者在多邊形邊上或者在其內。這個演算法是由數學大師葛立恆graham發明的,他曾經是美國數學學會ams att 首席科學家.see 模板 see include include using namespace std cl...
凸包問題 Graham掃瞄法
凸包點集q的凸包 convex hull 是指乙個最小凸多邊形,滿足q中的點或者在多邊形邊上或者在其內。右圖中由紅色線段表示的多邊形就是點集q 的凸包。頂點個數n 1 排序 在點集q中找最左下方的點p0,就是x座標和y座標都最小的點,其餘的點計算它們的極座標幅角,以幅角的非降序順序來排序,如果有幅角...