算法的大體思想如下:
首先將座標進行預排序,顯而易見,最左邊和最右邊的兩個點一定是凸包頂點,然後連接這兩點,然後這條線就可以把所有點分爲兩部分,我們將在這條線上方或者左側的點集稱作“上包”,下方或者右側的點集稱爲“下包”;不難分析出,在上包和下包中距離這條線最遠的點一定爲凸包頂點,因此,我們可以通過三角形面積的計算來尋找這個點,找到這個點後,我們就可以將這個點與之前找到的兩個點連接起來,從而得到範圍更小的上包和下包,遞歸的進行以上步驟直到上包或下包只有一個點時,算法結束,這時得到的點集就是凸包了。
怎麼來判斷點位於上包還是下包呢?幸運的是,我們可以直接利用三個點m(x1,y1) , n(x2,y2) , p(x3y3)的行列式的值來判斷點的相對位置:
|x1 y1 1|
|x2 y2 1| =x1y2 + x3y1 + x2y3 - x3y2 - x2y1 - x1y3;
|x3 y3 1|
當且僅當點p位於向量mn的左側時,這個行列式的值爲正
同時這個行列式的值絕對值的二分之一就是這三個點圍成的三角形的面積。
以下是個人總結的一些代碼
struct point
{
double x, y;
}pos[Max], ans[Max];
int t = 0;
double s[Max];
double Judge(point a, point b, point c)//僅當c點在向量左側時返回正
{
return a.x*b.y + c.x*a.y + b.x*c.y - c.x*b.y - b.x*a.y - a.x*c.y;
}
bool FindPos(point a, point b)
{
if (a.x < b.x || a.x == b.x&&a.y < b.y)//a在b的左或正下
return true;
else
return false;
}
void QuickHull(int begin, int end, point a, point b)
{
int x = begin, i = begin - 1, j = end + 1, k;
for (k = begin; k <= end; k++)
{
//s的絕對值爲面積的二分之一,面積最大點爲凸包頂點
if (s[k] > s[x] || s[k] == s[x] && FindPos(pos[x], pos[k]))
x = k;
}
point peak = pos[x];//頂點
for (k = begin; k <= end; k++)
{
s[++i] = Judge(pos[k], a, peak);
if (s[i] > 0)
swap(pos[i], pos[k]);//找出上包排在前面
else
i--;
}
for (k = end; k >= begin; k--)
{
s[--j] = Judge(pos[k], peak, b);
if (s[j] > 0)
swap(pos[j], pos[k]);//下包在後
else
j++;
}
if (begin <= i)
QuickHull(begin, i, a, peak);// 上包遞歸
ans[t++] = peak;
if (j <= end)
QuickHull(j, end, peak, b);//下包遞歸
}