凸包问题的Graham扫描法

格雷厄姆扫描法是利用平面上任意3 点所构成的回路是左转还是右转的判别法来求平面点集的凸包。
1.三角区符号的计算
点o(a,b),p(c,d),q(e,f)是平面上的任意三点,D=ab+cf+eb-bc-de-fa

当D 为正时,o,p,q,o 构成一个反时针方向的回路,即o,p,q,是左转的;当D 为负时,o,p,q,o 构成一个顺时针方向的回路,即o,p,q 是右转的;当D 为零时,此三点共线。

3.算法步骤:
(1)求平面点集S 中Y 坐标最小的点p0。
(2)以p0 为源点,变换S-{p0}中所有点的坐标
(3)以p0 为源点,计算S-{p0}中所有点的幅角。
(4)以幅角的非降排序S-{p0}中所有的点,令事件调度点T={p1,p2,…, pn-1}是排序过的数组。
(5)初始化堆栈:令CHS[0]=pn-1,CHS[1]=p0;令堆栈指针sp=1,事件调度点数组T的下标k=0。
(6)如果k<n-1,转步骤(7);否则,算法结束。
(7)计算CHS[sp -1], CHS[sp] = p0 ,T[k]所构成的三角区符号D,若D >= 0,sp = sp+1,CHS[sp] =T[k],k=k+1,转步骤(6);否则,sp=sp-1;转步骤(6)。

算法实现

struct Point
{
	int x;
	int y;
};

struct SPoint
{
	int x;
	int y;
	float ang; //tan值
};

void Sort(SPoint T[],int n)
{
	SPoint temp;
	for(int i=0;i<n;i++)
		for(int j=i+1;j<n;j++)
		{
			if(T[i].ang<T[j].ang)
			{
				temp=T[i];
				T[i]=T[j];
				T[j]=temp;
			}
		}
}

void Convex_hull(Point s[],Point CHS[],int n,int &sp)
{
	int i,k;
	float D;
	Point temp;
	SPoint T[18];
	for(i=1;i<n;i++)  //s中Y坐标最小的点与S[0]
		if((s[i].y<s[0].y) || ((s[i].y==s[0].y)  && s[i].x<s[0].x))
		{
			temp=s[0];
			s[0]=s[i];
			s[i]=temp;
		}
	for(i=1;i<n;i++) //以s[0]为源点,变换s[i]的坐标于T[i-1]
	{
		T[i-1].x=s[i].x-s[0].x;
		T[i-1].y=s[i].y-s[0].y;
		double at=T[i-1].y/T[i-1].x;
		T[i-1].ang=atan(at);
	}
	Sort(T,n-1); //按幅角大小排序
	CHS[0].x=T[n-2].x;
	CHS[0].y=T[n-2].y;
	CHS[1].x=0;
	CHS[1].y=0;
	sp=1;
	k=0;
	while(k<n-1) //求栈顶两点及扫描线上一点构成的三角符号
	{
		D=CHS[sp-1].x*CHS[sp].y+CHS[sp-1].y*T[k].x+CHS[sp].x*T[k].y-CHS[sp-1].y
			*CHS[sp].x-CHS[sp].y*T[k].x-T[k].y*CHS[sp-1].x;
		if(D>=0)
		{
			CHS[sp].x=T[k].x;
			CHS[sp].y=T[k].y;
			sp++;
			k++;
		}
		else
			sp--;
	}
}


 

你可能感兴趣的:(凸包问题的Graham扫描法)