首先,什么是凸包?
假设平面上有p0~p12共13个点,过某些点作一个多边形,使这个多边形能把所有点都“包”起来。当这个多边形是凸多边形的时候,我们就叫它“凸包”。如下图:
然后,什么是凸包问题?
我们把这些点放在二维坐标系里面,那么每个点都能用 (x,y) 来表示。
现给出点的数目13,和各个点的坐标。求构成凸包的点?
时间复杂度:O(n³)。
思路:两点确定一条直线,如果剩余的其它点都在这条直线的同一侧,则这两个点是凸包上的点,否则就不是。
步骤:
如何判断一个点 p3 是在直线 p1p2 的左边还是右边呢?(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3))
当上式结果为正时,p3在直线 p1p2 的左侧;当结果为负时,p3在直线 p1p2 的右边。
时间复杂度:O(n㏒n)。
思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题再分成几个更小的子问题……。然后我们就能用递归的方法,分别求这些子问题的解。最后把每个子问题的解“组装”成原来大问题的解。
步骤:
然而怎么求距离某直线最远的点呢?我们还是用到解一中的公式:
设有一个点 P3 和直线 P1P2 。(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3))
对上式的结果取绝对值,绝对值越大,则距离直线越远。
注意:在步骤一,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意。
时间复杂度:O(nH)。(其中 n 是点的总个数,H 是凸包上的点的个数)
思路:
注意:
时间复杂度:O(n㏒n)
思路:Graham扫描的思想和Jarris步进法类似,也是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,但它不是利用夹角。
步骤:
最后,栈中的元素就是凸包上的点了。
以下为用Graham扫描法动态求解的过程:
说真的,这个算法我也还没有看清。网上的资料也少的可怜,我暂且把网上的解释截个图在这里,往后搞懂以后再回来补上。
或者有人看懂了的,希望不吝指教,不甚感激!
以上讨论的只是二维的凸包,如果延生为三维、多维的凸包问题呢?如何求解?
不过首先,二维凸包可以用来解决围栏问题、城市规划问题、聚类分析等等。但是三维、多维的凸包可能的使用范畴有哪些?
#include
#include
int g_result[240][2];
/*getResult()实现功能:以坐标P0(x1,y1)和Pn(x2,y2)为直线,找出pack里面里这条直线最远的点Pmax *并找出直线P0Pmax和PmaxPn的上包,进行递归。 *注:Pack[0][0]存放点的个数,pack[1]开始存放点的坐标。 *全局变量g_result[][]用来存放凸包上的点,即最终所要的答案。同样g_result[0][0]存放的是已找到的点的个数。 **/
void getResult(int Pack[240][2], int x1, int y1, int x2, int y2)
{
int i,t,x3,y3,R,Rmax,tmax;
int ResultPack[240][2];
ResultPack[0][0] = 0;
if(Pack[0][0] <= 1)
return;
x3 = Pack[1][0];
y3 = Pack[1][1];
R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
Rmax = R;
tmax = 1;
for(i=2;i<=Pack[0][0];i++)
{
x3 = Pack[i][0];
y3 = Pack[i][1];
R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if(R >= 0)
{
t = ++ResultPack[0][0];
ResultPack[t][0] = x3;
ResultPack[t][1] = y3;
}
if(R > Rmax)
{
Rmax = R;
tmax = i;
}
}
if(Rmax <= 0)
{
for(i=1;i0][0];i++)
{
x3 = ResultPack[i][0];
y3 = ResultPack[i][1];
R = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if(R == 0 && !((x3==x2&&y3==y2)||(x3==x1&&y3==y1)))
{
t = ++g_result[0][0];
g_result[t][0] = ResultPack[i][0];
g_result[t][1] = ResultPack[i][1];
}
}
return;
}
else
{
t = ++g_result[0][0];
g_result[t][0] = Pack[tmax][0];
g_result[t][1] = Pack[tmax][1];
if(ResultPack[0][0] == 0)
return;
}
getResult(ResultPack,x1,y1,Pack[tmax][0],Pack[tmax][1]);
getResult(ResultPack,Pack[tmax][0],Pack[tmax][1],x2,y2);
}
void main()
{
int Point[240][2];//Point存所有点。
int i=1;
int x1,y1,x2,y2,x3,y3;
g_result[0][0]=0;Point[0][0]=0;//Point的第一行第一列元素存放包里面有几个点。初始化为0。
printf("请输入所有点的坐标:\n");
while(scanf("%d,%d",&Point[i][0],&Point[i][1]) != EOF)
i++;
Point[0][0] = i-1;
x1 = Point[1][0];
y1 = Point[1][1];
x2 = x1;
y2 = y1;
for(i=2;i<=Point[0][0];i++)
{
x3 = Point[i][0];
y3 = Point[i][1];
if(x3 < x1)
{
x1 = x3;
y1 = y3;
}
else if(x3 > x2)
{
x2 = x3;
y2 = y3;
}
}
g_result[1][0] = x1;
g_result[1][1] = y1;
g_result[2][0] = x2;
g_result[2][1] = y2;
g_result[0][0] += 2;
getResult(Point, x1, y1, x2, y2);
getResult(Point, x2, y2, x1, y1);
printf("\n\n构成凸包的点有:\n");
for(i=1;i<=g_result[0][0];i++)
printf("(%d,%d)\n",g_result[i][0],g_result[i][1]);
system("pause");
}