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

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

http://softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm

实现如下:

#include <iostream>

#include <math.h>

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<nums;i++)

    {

        if(s[i].y<s[0].y || (s[i].y==s[0].y && s[i].x<s[0].x))

            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<nums;i++)

    {

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

            break;

    }

    for(j=1;j<(i+1)/2;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<end-1)

    {

        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<nums;i++)

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

        return;

    }

    int top=0;

    int i;

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

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

    while(i<nums)

    {

        //用<号判断则包含凸包边上的共线点,<=号判断则只包含凸包顶点

        if(CrossMul(result[top-2],result[top-1],s[i])<=0)

        {

            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<<"convex hull is:"<<endl;

    Point result[18];

    int nums;

    ConvexHull(pa,18,result,nums);

    for(int i=0;i<nums;i++)

        cout<<result[i].x <<"," <<result[i].y<<endl;

    return 0;

}

经验证,算法无误。

最新文章

  1. paintEvent(QPaintEvent*)是系统自动调用的
  2. IBC编程社区
  3. 用修改hosts的方式来屏蔽某些网站
  4. [创业中, 寻求合作] 业务方向:车联网智能终端;APP蓝牙控制汽车;APP网络远程控制汽车 (联系电话:18503086002)
  5. Go语言的GOPATH与工作目录详解
  6. NopCommerce 关于Customer的会员类别及会员价处理 的尝试途径
  7. DisJSet:食物链(POJ 1182)
  8. C#中listbox中选中多项,并删除
  9. extjs 简单入门
  10. linux device driver —— 环形缓冲区的实现
  11. openStack工具集
  12. Oracle 快照及 dblink使用 (两台服务器数据同步)
  13. 常用 Git 命令清单
  14. 利用10h号中断在dos中间显示自己名字
  15. MAC下secretCRT使用技巧(转)
  16. pytest之收集用例规则与运行指定用例
  17. 认识volatile的工作原理
  18. 如何让.net程序支持TLS1.2
  19. Oracle存储过程中使用临时表
  20. java web 大文件下载

热门文章

  1. mysql 知识整理
  2. linux 命令技巧(转)--history
  3. 微信获取用户列表的json字符串解析
  4. 第二章 Vue快速入门--8 v-bind指令的学习
  5. 闭包-IIFE
  6. github高速下载的方法
  7. 第十一章 前端开发-jQuery
  8. Python入门-2编程基本概念:03引用的本质-栈内存和堆内存-内存示意图
  9. [人物存档]【AI少女】【捏脸数据】1223今日份的推荐
  10. sh_20_for语法演练