继上一篇凸包问题的蛮力解法,本篇将介绍凸包问题的GrahamScan解法。
首先了解一下GrahamScan解法的原理:
当沿着
Convex hull
逆时针漫游时,总是向左转
在极坐标系下按照极角大小排列,然后逆时针
方向漫游点集,去除非
Convex hull
顶点
(
非左
转点
)
。
第一步
这里P0是极坐标的中心,在编程的时候,经常就是选择y坐标最低的点
第二步:
这样就建立起了所有点到P0的极坐标
第三步:
这里首先选择三个点压栈,这三个点肯定构成的是非右转关系
看P3
可以发现P3和P2P1构成右转关系,因此考虑把P2从凸包点中排除,然后把P3加入到凸包栈中,继续向前探进
第四步:
这里P3P4P5都不构成右转,因此都放入到凸包栈中,
第五步:
P6P5P4构成右转关系,因此需要删除P5,加入P6,
P7P6P4构成右转关系,因此删除P6,加入P7,P7P4P3构成非右转关系,删除P4。。。。。。。
知道所有点都遍历完
第七步:
通过上面的几个步骤相信大家已经领悟到其中的奥妙了,下面贴出主要代码:
/* 计算凸包 */
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和分治的结合算法。