关于凸包——Graham扫描法

来源:互联网 发布:linux百度翻译 编辑:程序博客网 时间:2024/05/22 08:00

首先是凸包的第一种解法,graham扫描法。算法的步骤如下:

1 首先我们要找到凸包的一个顶点,这个顶点的y坐标要最小,相同的y的情况下,选择更靠左的点

2 对给出的顶点集进行排序,按照极角递增的顺序进行排序?如何进行操作呢,我们可以用我们矢量积的性质,如果矢量积为正,b矢量在a矢量的逆时针方向。极角顺序相同的,我们把距离近的那一个排在前面。

3 完成排序后,我们将p0,p1入栈,对i=2 to n进行如下操作:如果ceross(p[top-1],p[top],p[i]<=0),就将当前栈顶元素出栈。随后将新的元素加入到栈中。

这样我们就完成了求解凸包的过程。

之后就是各种应用,比如求凸包的周长等。下面是代码:

int cross(point p0,point p1,point p2)//p0p1 和p0p2的叉积 
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}

double dis(point p0,point p1)//计算距离的绝对值就好 
{
return sqrt(double((p1.x-p0.x)*(p1.x-p0.x)+(p1.y-p0.y)*(p1.y-p0.y)));
}

bool cmp(point p1,point p2)
{
int tmp=cross(list[0],p1,p2);//计算叉积
if(tmp>0)
 return 1;
else if(tmp==0)//在同一条线上,角度相同,则距离小的在前面 
  return dis(list[0],p1)<dis(list[0],p2);
else
 return 0;
 
}

void init(int n)//寻找凸包的顶点,并极角的排序 
{
int k=0;
point p0;
scanf("%d%d",&list[0].x,&list[0].y);
p0.x=list[0].x;
p0.y=list[0].y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&list[i].x,&list[i].y);
if(p0.y>list[i].y||(p0.y==list[i].y&&p0.x>list[i].x))
{
p0.x=list[i].x;
p0.y=list[i].y;
k=i;
}
}
list[k]=list[0];
list[0]=p0;
sort(list+1,list+n,cmp);

}

void graham(int n)//graham扫描法 
{
if(n==1)
{
top=0;
stack[0]=0;
}
if(n==2)
{
top=1;
stack[0]=0;
stack[1]=1;
}
if(n>2)
{
for(int i=0;i<=1;i++)//将p0,p1进栈 
 stack[i]=i;
top=1;
for(int i=2;i<n;i++)
{
while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0)
 top--;//不符合条件,出栈
top++;
stack[top]=i; 
}
}
}

//求凸包的周长

for(int i=0;i<top;i++)
 res+=dis(list[stack[i]],list[stack[i+1]]);
res+=dis(list[stack[0]],list[stack[top]]);//凸包的周长

时间复杂度为O(NlogN) N为顶点集的数量