graham算法 java_凸包Graham Scan算法实现

凸包算法实现点集合中搜索凸包顶点的功能,可以处理共线情况,可以输出共线点也可以不输出而只输出凸包顶点。经典的Graham Scan算法,点排序使用极角排序方式,并对共线情况做特殊处理。一般算法是将共线的点去掉距离小的,保留最远的,这样处理会导致不能输出凸包边上的点,只能输出顶点。但是有时候需要输出这些边上的点,因此这里我将共线点都保留,并按照顺序排列。共线点排列方式是:非起始边按照从远道近排列,起始边按从近到远排列。

算法原理参见如下网址,讲解很详细:

实现如下:

#include

#include

using namespace std;

typedef struct{double x,y;} Point;

void qsortpoint(Point s[],Point base,int start,int end);

void sortstartedge(Point s[],int nums);

//向量(x1,y1),(x2,y2)的叉积double CrossMul(double x1,double y1,double x2,double y2)

{

return x1*y2-x2*y1;

}

//向量(x1,y1),(x2,y2)的点积

double DotMul(double x1,double y1,double x2,double y2)

{

return x1*x2+y1*y2;

}

//跨立判断

//判断点c是在向量ab的逆时针方向还是顺时针方向,大于零逆时针,等于0则共线double CrossMul(Point a,Point b,Point c)

{

return CrossMul(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);

}

//计算向量ab和ac点积double DotMul(Point a,Point b,Point c)

{

return DotMul(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);

}

//判断浮点数符号

int doublecmp(double d)

{

if(fabs(d)<10e-6)

return 0;

return d>0?1:-1;

}

//判断同一直线上的三个点位置,点c是否在点ab之间bool betweenCmp(Point a,Point b,Point c)

{

if(doublecmp(DotMul(c,a,b))<=0)

return true;

return false;

}

//判断j是否在base->i向量的左边或当共线时j是否位于它们的线段之间bool isLeftorNearer(Point base,Point i,Point j)

{

if(CrossMul(base,i,j)>0)

return true;

if(CrossMul(base,i,j)==0 && betweenCmp(base,i,j))

return true;

return false;

}

void swap(Point& a,Point& b)

{

Point temp = b;

b=a;

a=temp;

}

//以s中的最低点为参考点,对其他所有点进行极角排序(逆时针)

//共线时离参考点较远的点排在前面,凸包的起始边共线点从近到远排列void sortpoint(Point s[],int nums)

{

//找最低点

for(int i=1;i

{

if(s[i].y

swap(s[0],s[i]);

}

qsortpoint(s,s[0],1,nums);

//将起始边上的共线点重新排列    sortstartedge(s,nums);

}

void sortstartedge(Point s[],int nums)

{

int i,j;

for(i=2;i

{

if(CrossMul(s[0],s[1],s[i])!=0)

break;

}

for(j=1;j

swap(s[j],s[i-j]);

}

//将点按极角逆时针排序void qsortpoint(Point s[],Point base,int start,int end)

{

if(start>=end)

return;

Point partition = s[end-1];

int i=start-1,j=start-1;

while(++j

{

if(isLeftorNearer(base,s[j],partition))

{

swap(s[++i],s[j]);

}

}

swap(s[++i],s[end-1]);

qsortpoint(s,base,start,i);

qsortpoint(s,base,i+1,end);

}

void ConvexHull(Point s[],int nums,Point result[],int& resultnums)

{

sortpoint(s,nums);

resultnums = 0;

if(nums<=3)

{

for(int i=0;i

result[resultnums++] = s[i];

return;

}

int top=0;

int i;

for(i=0;i<2;i++)

result[top++] = s[i];

while(i

{

//用

{

top--;

}

else

{

result[top++] = s[i++];

}

}

//最后加入起点形成闭包    while(CrossMul(result[top-2],result[top-1],s[0])<=0)

{

top--;

}

result[top++]=s[0];

resultnums = top;

}

int main()

{

Point pa[] = {{0,0},{1,0},{2,0},{3,0},{4,0},

{4,1},{4,2},{4,3},{4,4},

{3,4},{2,4},{1,4},{0,4},

{0,3},{0,2},{0,1},{2,2},{1,1}};

cout<

Point result[18];

int nums;

ConvexHull(pa,18,result,nums);    for(int i=0;i

cout<

return 0;

}

经验证,算法无误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值