凸包问题之GrahamScan解法

本文详细介绍了GrahamScan算法的基本原理及其实现过程。该算法通过建立极坐标系并按照极角大小排序点集来确定凸包。具体步骤包括选择极坐标中心、按极角排序、压栈处理以及去除非凸顶点等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

继上一篇凸包问题的蛮力解法,本篇将介绍凸包问题的GrahamScan解法。

首先了解一下GrahamScan解法的原理:

当沿着 Convex hull 逆时针漫游时,总是向左转 在极坐标系下按照极角大小排列,然后逆时针 方向漫游点集,去除非 Convex hull 顶点 ( 非左  转点 )

第一步



这里P0是极坐标的中心,在编程的时候,经常就是选择y坐标最低的点

第二步:


这样就建立起了所有点到P0的极坐标

第三步:


这里首先选择三个点压栈,这三个点肯定构成的是非右转关系

看P3


可以发现P3和P2P1构成右转关系,因此考虑把P2从凸包点中排除,然后把P3加入到凸包栈中,继续向前探进

第四步:


这里P3P4P5都不构成右转,因此都放入到凸包栈中,

第五步:


P6P5P4构成右转关系,因此需要删除P5,加入P6,


P7P6P4构成右转关系,因此删除P6,加入P7,P7P4P3构成非右转关系,删除P4。。。。。。。

知道所有点都遍历完

第七步:


上图构成的就是凸包了(包括P0)

通过上面的几个步骤相信大家已经领悟到其中的奥妙了,下面贴出主要代码:

/* 计算凸包 */
void CalculateConvexHull2(struct Point *pArray,struct Polar *prArray,int length)
{
	//初始化
	InitializePolarCoordinates(pArray,prArray,length);

	struct Polar *start=prArray;

	//栈中最上面的两个点 
	struct Polar *p1=NULL,*p2=NULL;
	top=0;
	
	//最小的三个点压栈

	stack[top++]=start++;
	stack[top++]=start++;
	stack[top++]=start++;	

	while(start!=prArray+length) {
		//获取p1和p2
		p2=stack[top-1];
		p1=stack[top-2];

		//右转,弹出p2,压入p1
		while(IsRightTuring(start->p,p2->p,p1->p) > 0 ) {
			if(top<=2)
				break;
			top--;
			p2=stack[top-1];
			p1=stack[top-2];
		}

		//压入新的点
		stack[top++]=start++;
	}
	
}

/* 打印凸包点 */
void PrintConvexHull2(struct Point *pArray,struct Polar *prArray,int &length)
{
	CalculateConvexHull2(pArray,prArray,length);

	using std::cout;
	using std::endl;

	for(int i=0;i<top;i++) {
		cout<<stack[i]->p->x<<","<<stack[i]->p->y<<endl;
	}
	//更新长度
	length=top;
}

其中Point就是直角坐标系的点,而Polar是对应的极坐标系的点,因此我们还需要建立极坐标系,建立极坐标系的关键就是找极坐标中心

下面贴而出相关代码:

/* 排序选择Y轴最低的坐标点作为极坐标中心 */
static struct Point ChooseCentroid(struct Point *pArray,int length)
{
	qsort(pArray,length,sizeof(struct Point),PointYCompare);
	return pArray[0];
}

/* 直角坐标转化为极坐标 */
void PointToPolar(struct Point * ptArray,struct Polar *prArray,int length,struct Point centroid)
{
	for(int i=0;i<length;i++) {
		//在极坐标横轴上
		if(ptArray[i].y==centroid.y) {
			if(ptArray[i].x>=centroid.x)
				prArray[i].angle=0;
			else
				prArray[i].angle=180;
		}
		//90度
		else if(ptArray[i].x==centroid.x)
			prArray[i].angle=90;
		//其他
		else
			prArray[i].angle=arctan(ptArray[i],centroid);

		//长度暂时不用

		//指针指向
		prArray[i].p=&ptArray[i];
	}
}

/* 初始化极坐标系 */
static void InitializePolarCoordinates(struct Point *pArray,struct Polar *prArray,int length)
{
	//找极坐标中心
	struct Point centroid=ChooseCentroid(pArray,length);

	//直角坐标转化为极坐标
	PointToPolar(pArray,prArray,length,centroid);

	//极坐标排序
	PolarQuickSort(prArray,0,length-1);
}

大家可以参考上述代码,其他非关键函数相信大家都能补全,就不再贴出了。

下一篇将介绍凸包问题的GrahamScan和分治的结合算法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值