使用Graham扫描法求二维凸包的一个程序

  1 #include "includeall.h"
  2 #include "Link.class.h"
  3
  4 int RightOrLeft(float x1,float y1,float x2,float y2,float x3,float y3)//判断第三个点在前两个点连成的直线的哪个位置,-1 左边,0,直线上,1 右边
  5 {
  6     float X=(y3-y1)*(x2-x1)/(y2-y1)+x1;
  7     if(X<x3)
  8     {
  9         return 1;
 10     }
 11     else if(X>x3)
 12     {
 13         return -1;
 14     }
 15     else{
 16         return 0;
 17     }
 18 }
 19
 20
 21 /*
 22 *该函数对link的所有点进行二维凸包运算
 23 *link中的每个节点保存了一个点的三维坐标,二维凸包运输只会选取其中的两个坐标进行运算
 24 *具体选取哪两个坐标由该函数的type参数决定
 25 *type的合法取值为 3 5 6,取三代表选取yz坐标(3的二进制为011),取5代表选取xz坐标,取6代表选取xy坐标
 26 *执行完凸包运算之后,link中的节点将被修改,所以如有必要,应该手动在调用该函数之前备份参数链表
 27 *运算执行完之后,link的节点的z坐标将被清零,xy坐标将依据type的取值,对应于原始链表的某两个坐标
 28 *
 29 */
 30 void convelHull(Link * link,int type=5)//对link中的元素进行二维凸包运算,执行后link中只有x,y值有效
 31 {
 32     if(link->count()<3)
 33     {
 34         printf("待求凸包的链表中至少有三个元素\n");
 35         exit(-1);
 36     }
 37     Link * link_tmp=new Link();
 38     {//对利用link的某两个坐标构造link_tmp
 39         Node * tmp=link->pointOfSpecificElement(1);
 40         if(type==3)
 41         {
 42             while(tmp!=NULL)
 43             {
 44                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
 45                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 46                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
 47                 link_tmp->add(tmp->y,tmp->z,0.0);
 48                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
 49                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 50                 tmp=tmp->next;
 51             }
 52         }
 53         else if(type==5)
 54         {
 55             while(tmp!=NULL)
 56             {
 57                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
 58                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 59                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
 60                 link_tmp->add(tmp->x,tmp->z,0.0);
 61                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
 62                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 63                 tmp=tmp->next;
 64             }
 65         }
 66         else if(type==6)
 67         {
 68             while(tmp!=NULL)
 69             {
 70                 //此处将link的三维坐标系的某两个映射到函数内使用的link_tmp中,之后的操作将对link_tmp的x,y成员进行操作
 71                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 72                 //可以在此处修改需要对原始的link链表中的哪两个坐标进行凸包运算
 73                 link_tmp->add(tmp->x,tmp->y,0.0);
 74                 //进行凸包运算之后,link的z坐标将被清零,例如此处,link的xy坐标分别对应原始的xz坐标
 75                 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 76                 tmp=tmp->next;
 77             }
 78         }
 79         else{
 80             printf("参数不符合规范\n");
 81             exit(-1);
 82         }
 83     }//link_tmp已经构造完成
 84     {//将link_tmp中y坐标最小的点放到链表的第一个位置
 85         Node * tmp=link_tmp->pointOfSpecificElement(0);
 86         Node * tmp2=tmp;
 87         while(tmp!=NULL && tmp->next!=NULL)
 88         {
 89             if(tmp->next->y<tmp2->next->y)
 90             {
 91                 tmp2=tmp;
 92             }
 93             tmp=tmp->next;
 94         }//tmp2指向y坐标最小的节点的前一个节点
 95         link_tmp->add(tmp2->next->x,tmp2->next->y,tmp2->next->z);
 96         link_tmp->del(tmp2);
 97     }//y坐标最小的元素已经是链表的第一个节点
 98     Node trans;//保存此时平移的距离,以便于最后把它们平移回来
 99     {//所有点平移,使link_tmp第一个节点的坐标是坐标原点
100         trans.x=link_tmp->pointOfSpecificElement(1)->x;
101         trans.y=link_tmp->pointOfSpecificElement(1)->y;
102         //trans.z=link_tmp->pointOfSpecificElement(1)->z;
103         Node * tmp=link_tmp->pointOfSpecificElement(1);
104         while(tmp!=NULL)
105         {
106             tmp->x-=trans.x;
107             tmp->y-=trans.y;
108             //tmp->z-=trans.z;
109             tmp=tmp->next;
110         }
111     }//所有点的平移完成
112
113     {//从第二个点开始按与第一个点的距离从小到大排序
114         Node * tmp1=link_tmp->pointOfSpecificElement(0);
115         Node * tmp2=tmp1->next;
116         while(tmp2!=NULL)
117         {
118             //使用z成员保存其到原点的距离
119             tmp2->z=sqrt((tmp2->x* tmp2->x)+(tmp2->y*tmp2->y));
120             tmp2=tmp2->next;
121         }
122         tmp2=tmp1->next;
123         while(tmp2!=NULL && tmp2->next!=NULL)
124         {//保证只有第一个点在坐标原点
125             if(tmp2->next->z==0.0)
126             {
127                 link_tmp->del(tmp2);
128                 continue;
129             }
130             tmp2=tmp2->next;
131         }
132         if(link_tmp->count()<3)
133         {
134             printf("对链表进行去重操作之后导致链表中剩余元素不足三个,无法进行后续运算\n");
135             exit(-1);
136         }
137         tmp2=tmp1->next->next;
138         while(tmp2!=NULL)
139         {
140             Node * minDis=tmp2;
141             Node * tmp3=tmp2;
142             while(tmp3!=NULL)
143             {
144                 if(tmp3->z<minDis->z)
145                 {
146                     minDis=tmp3;
147                 }
148                 tmp3=tmp3->next;
149             }
150             if(minDis!=tmp2)
151             {
152                 Node tmp;
153                 tmp.x=minDis->x;
154                 tmp.y=minDis->y;
155                 tmp.z=minDis->z;
156                 minDis->x=tmp2->x;
157                 minDis->y=tmp2->y;
158                 minDis->z=tmp2->z;
159                 tmp2->x=tmp.x;
160                 tmp2->y=tmp.y;
161                 tmp2->z=tmp.z;
162             }
163             tmp2=tmp2->next;
164         }
165     }//按照距离排序完成
166     {//从第二个点开始按与第一个点的幅角从小到大排序
167         Node * tmp1=link_tmp->pointOfSpecificElement(1);
168         Node * tmp2=tmp1->next;
169         while(tmp2!=NULL)
170         {
171             //使用z成员保存其幅角
172             tmp2->z=acos((tmp2->x/tmp2->z));
173             tmp2=tmp2->next;
174         }
175         tmp2=tmp1->next;
176         while(tmp2!=NULL)
177         {
178             Node * minAng=tmp2;
179             Node * tmp3=tmp2;
180             while(tmp3!=NULL)
181             {
182                 if(tmp3->z<minAng->z)
183                 {
184                     minAng=tmp3;
185                 }
186                 tmp3=tmp3->next;
187             }
188             if(minAng!=tmp2)
189             {
190                 Node tmp;
191                 tmp.x=minAng->x;
192                 tmp.y=minAng->y;
193                 tmp.z=minAng->z;
194                 minAng->x=tmp2->x;
195                 minAng->y=tmp2->y;
196                 minAng->z=tmp2->z;
197                 tmp2->x=tmp.x;
198                 tmp2->y=tmp.y;
199                 tmp2->z=tmp.z;
200             }
201             tmp2=tmp2->next;
202         }
203     }//按照幅角排序完成
204     {//对其进行求凸包运算
205         Link * stk_tmp=new Link();
206         Node * tmp=link_tmp->pointOfSpecificElement(1);
207         stk_tmp->add(tmp->x,tmp->y,0.0);
208         tmp=tmp->next;
209         stk_tmp->add(tmp->x,tmp->y,0.0);
210         Node * stkTop=stk_tmp->pointOfSpecificElement(1);//指向栈顶元素
211         Node * stkNext=stkTop->next;//指向栈顶的下一个元素
212         Node * current=tmp->next;//指向当前点
213         while(1)
214         {
215             if(RightOrLeft(stkNext->x,stkNext->y,stkTop->x,stkTop->y,current->x,current->y)==1)
216             {
217                 stk_tmp->del(stk_tmp->pointOfSpecificElement(0));
218                 stkTop=stk_tmp->pointOfSpecificElement(1);
219                 stkNext=stkTop->next;
220             }
221             else{
222                 stk_tmp->add(current->x,current->y,0.0);
223                 stkTop=stk_tmp->pointOfSpecificElement(1);
224                 stkNext=stkTop->next;
225                 if(current->next==NULL) break;
226                 else{
227                     current=current->next;
228                 }
229             }
230         }//end of while
231         //现在栈 stk_tmp中保存有凸包上的点
232         {//对凸包上的点平移到原位置
233             Node * tmp=stk_tmp->pointOfSpecificElement(1);
234             while(tmp!=NULL)
235             {
236                 tmp->x+=trans.x;
237                 tmp->y+=trans.y;
238                 //tmp->z+=trans.z;
239                 tmp=tmp->next;
240             }
241         }//平移到原位置完成
242         delete link_tmp;
243         link->clear();
244         link->copy(stk_tmp);
245         delete stk_tmp;
246     }//凸包运算完成
247 }// end of function convelHull
248 int main()
249 {
250     Link * link=new Link();
251     link->add(0,1,0);
252     link->add(0,1,1);
253     link->add(1,1,0);
254     link->add(1,1,1);
255     link->add(2,1,2);
256     link->add(5,1,1);
257
258     Node * tmp=link->pointOfSpecificElement(1);
259     printf("未进行凸包运算的点(%d):\n",link->count());
260     while(tmp!=NULL)
261     {
262         printf("%f %f %f\n",tmp->x,tmp->y,tmp->z);
263         tmp=tmp->next;
264     }
265     printf("\n");
266     convelHull(link,5);
267      tmp=link->pointOfSpecificElement(1);
268     printf("进行过凸包运算的点(%d):\n",link->count());
269     while(tmp!=NULL)
270     {
271         printf("%f %f %f\n",tmp->x,tmp->y,tmp->z);
272         tmp=tmp->next;
273     }
274     printf("\n");
275     return 0;
276 }

main.cpp

  1 #include "includeall.h"
  2 typedef struct node{//一维链表使用的
  3     float x, y, z;
  4     struct node * next;
  5 }Node;
  6
  7 class Link{//有头节点的链表类
  8 private:
  9
 10 public:
 11     Node * data=NULL;
 12 private:
 13     void init()//初始化链表
 14     {
 15         this->data=NULL;
 16         this->data=new Node;
 17         this->data->x=this->data->y=this->data->z=0.0;
 18         this->data->next=NULL;
 19     }
 20
 21     void destroy()//清除链表占用的空间,包括头节点,所以之后链表不能再继续使用
 22     {
 23         this->clear();
 24         delete this->data;
 25         this->data=NULL;
 26     }
 27
 28     void check()//检验链表是否有效
 29     {
 30         if(this->data==NULL)
 31         {
 32             cout<<"链表未正确初始化或已经被销毁"<<endl;
 33             exit(0);
 34         }
 35     }
 36
 37 public:
 38
 39     void add(float x,float y,float z)//增加一个节点
 40     {
 41         this->check();
 42         Node * tmp=new Node;
 43         tmp->x=x;tmp->y=y;tmp->z=z;
 44         tmp->next=this->data->next;
 45         this->data->next=tmp;
 46     }
 47
 48     void del(Node * prep)//删除一个节点
 49     {
 50         this->check();
 51         Node * tmp=prep->next;
 52         prep->next=prep->next->next;
 53         delete tmp;
 54     }
 55
 56     int count()//统计链表中节点的数目
 57     {
 58         this->check();
 59         int i=0;
 60         Node * tmp=this->data->next;;
 61         while(tmp!=NULL)
 62         {
 63             tmp=tmp->next;
 64             ++i;
 65         }
 66         return i;
 67     }
 68
 69     Node * pointOfSpecificElement(int n)//获取指向特定第几个元素的指针
 70     {
 71         Node * tmp=this->data;
 72         while(tmp!=NULL)
 73         {
 74             if(n<=0) break;
 75             --n;
 76             tmp=tmp->next;
 77         }
 78         return tmp;
 79     }
 80
 81     void clear()//清空链表中的所有元素,不包括头节点
 82     {
 83         this->check();
 84         while(!this->empty())
 85         {
 86             this->del(this->data);
 87         }
 88     }
 89
 90     void copy(Link * link,int n=1)//将参数链表中从第n个开始的元素拷贝增加到this链表,头节点作为第0个
 91     {
 92         Node * tmp=link->pointOfSpecificElement(n);
 93         while(tmp!=NULL)
 94         {
 95             this->add(tmp->x,tmp->y,tmp->z);
 96             tmp=tmp->next;
 97         }
 98     }
 99
100
101     bool empty()//链表是否为空
102     {
103         this->check();
104         return (this->data->next==NULL);
105     }
106
107     Link()
108     {
109         this->init();
110     }
111     virtual ~Link()
112     {
113         this->destroy();
114     }
115 };

Link.class.h

时间: 2024-10-12 00:50:24

使用Graham扫描法求二维凸包的一个程序的相关文章

(模板)poj1113(graham扫描法求凸包)

题目链接:https://vjudge.net/problem/POJ-1113 题意:简化下题意即求凸包的周长+2×PI×r. 思路:用graham求凸包,模板是kuangbin的. AC code: #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int maxn=1005; const double PI=

计算几何 二维凸包问题 Andrew算法

凸包:把给定点包围在内部的.面积最小的凸多边形. Andrew算法是Graham算法的变种,速度更快稳定性也更好. 首先把所有点排序,按照第一关键字x第二关键字y从小到大排序,删除重复点后得到点序列P1...Pn. 1)把P1,P2放入凸包中,凸包中的点使用栈存储 2)从p3开始,当下一个点在凸包当前前进方向(即直线p1p2)左边的时候继续: 3)否则依次删除最近加入凸包的点,直到新点在左边. 如图,新点P18在当前前进方向P10P15的右边(使用叉积判断),因此需要从凸包上删除点P15和P10

二维凸包模板

double cross(Point a,Point b) { return a.x*b.y-a.y*b.x; } double mul(Point p0,Point p1,Point p2) { return cross(p1-p0,p2-p0); } double dis(Point a) { return sqrt(a.x*a.x+a.y*a.y); } bool cmp(Point a,Point b) { if(dcmp(mul(p[0],a,b))==0) return dis(a-

二维凸包

二维凸包模板 p[1010]//输入的点集,res[1010]//输出的点集 int n;//点的个数 int cmp(Point a,Point b)//先对x坐标排序,在比较y坐标 { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } int ConvexHull()//返回凸包顶点数 { sort(p,p+n,cmp); int m=0; for(int i=0;i<=n-1;i++) { while(m>1&&Cr

Luogu 2742 二维凸包

Luogu 2742 二维凸包 使用 \(Andrew\) 算法.将点排序后分别求上下凸壳,用单调栈维护. 利用向量叉积来判断当前方向.若 \(v_1\times v_2<0\) ,说明 \(v_2\) 在 \(v_1\) 的右侧, \(<0\) 为左侧, \(=0\) 说明二者共线. 参考讲解. #include<bits/stdc++.h> using namespace std; #define ll long long #define mp make_pair #defin

结对开发(求二维首尾相接数组的最大子数组和)

一.题目要求 输入一个二维整形数组,数组里有正数也有负数. 二维数组首尾相接,象个一条首尾相接带子一样. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值. 要求时间复杂度为O(n)题目:返回一个二维整数数组中最大子数组的和 一.设计思想 求环形二维数组最大子数组的和,可以转化为求一维数组最大子数组的和 我们有一个最初的二维数组a[n][n]找它的 最大子数组之和 1.我们先建立一个新的二维数组b[n][2*n-1], 2,这个新的二维数组就是将初始的二

求二维数组中子数组和中最大的值,及子数组

求二维数组中子数组和中最大的值,及子数组 个人信息:就读于燕大本科软件工程专业 目前大三; 本人博客:google搜索"cqs_2012"即可; 个人爱好:酷爱数据结构和算法,希望将来从事算法工作为人民作出自己的贡献; 编程语言:C++ ; 编程坏境:Windows 7 专业版 x64; 编程工具:vs2008; 制图工具:office 2010 powerpoint; 硬件信息:7G-3 笔记本; 真言 每次着急写程序,碰到问题就头疼,头疼之后便是满满的收获,付出总有回报. 题目 求

软件工程结对开发之求二维数组中连续最大子数组之和2

一.题目要求 题目:返回一个二维整数数组中最大子数组的和. 要求: 输入一个二维整形数组,数组里有正数也有负数. 二维数组首尾相接,象个一条首尾相接带子一样. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值.要求时间复杂度为O(n). 二.设计思路 先调用以前求二维最大连续子数组之和的maxSubArray函数求一个首尾不相邻的二维最大连续子数组之和,接着用将第k列各元素左移一列可以再求一个最大连续子数组之和 ,循环m次(因为原二维数组有m列)求得每个

结对开发——求二维环形数组所有子矩阵最大和的问题

一.题目要求: 输入一个二维整形数组,数组里有正数也有负数. 二维数组首尾相接,象个一条首尾相接带子一样. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值. 要求时间复杂度为O(n)题目:返回一个二维整数数组中最大子数组的和. 二.解决思路: 由于上次我们做过求二维数组最大子矩阵和的问题,又做了求一维环状数组的子数组最大值问题,这次就在以前的基础上进行修改,先对二维数组进行了重构,形成一个环状二维数组,然后再用求二维数组子矩阵最大和的方法求得最终结果.