算法的大体思想如下:
首先将坐标进行预排序,显而易见,最左边和最右边的两个点一定是凸包顶点,然后连接这两点,然后这条线就可以把所有点分为两部分,我们将在这条线上方或者左侧的点集称作“上包”,下方或者右侧的点集称为“下包”;不难分析出,在上包和下包中距离这条线最远的点一定为凸包顶点,因此,我们可以通过三角形面积的计算来寻找这个点,找到这个点后,我们就可以将这个点与之前找到的两个点连接起来,从而得到范围更小的上包和下包,递归的进行以上步骤直到上包或下包只有一个点时,算法结束,这时得到的点集就是凸包了。
怎么来判断点位于上包还是下包呢?幸运的是,我们可以直接利用三个点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);//下包递归
}