凸包的形成(C++双向循环链表描述)

#include<iostream>
#include<fstream>//ifstream/ostream的头文件,文件流
#include<sstream>//string流,istringstream,ostringstream
#include "doublychain.h"
#include "assert.h"//assert的头文件
using namespace std;

//凸包的形成
void GeConvexHull(doublychain<pointInFlat>& S)
{
       //任取两点画直线,检查其他点是否都在该直线上,若是则共线,求出两端点,反之则求出凸包。
	    const float PI = 3.1415926;
		chainNode<pointInFlat>*f, *s, *p;
		f = S.firstNode;
		s = f->next;
		float x1 = (f->element).x;
		float y1 = (f->element).y;
		float x2 = (s->element).x;
		float y2 = (s->element).y;
		float a = x2 - x1;
		float b = y2 - y1;
		float x = x1;
		float y = y1;
		p = s->next;
		while (a*(y - y1) - b*(x - x1) == 0 && p !=S.firstNode)
		{
			x = (p->element).x;
			y = (p->element).y;
			p = p->next;
		}
		if (p == S.firstNode)//共线,有两种情况,垂直和不垂直,垂直时两端点的y值不一样,不垂直时只要求出横坐标的极值就得出两端点
		{
			pointInFlat min, max;
			chainNode<pointInFlat>*pt = S.firstNode;
			if (a == 0)//共线直线垂直于x轴
			{
				min.y = (pt->element).y;   //纵坐标最小的那个点
				max.y = (pt->element).y;    //纵坐标最大的那个点
				do                      //while (pt != NULL)
				{
					if (min.y >= (pt->element).y)
						min.y = (pt->element).y;
					if (max.y <= (pt->element).y)
						max.y = (pt->element).y;
					pt = pt->next;
				} while (pt != S.firstNode);
			}
			else                   //共线直线没有垂直于x轴
			{
				min.x = (pt->element).x;   //纵坐标最小的那个点
				max.x = (pt->element).x;    //纵坐标最大的那个点
				do                          //while (pt != NULL)
				{
					if (min.x >= (pt->element).x)
						min.x = (pt->element).x;
					if (max.x <= (pt->element).x)
						max.x = (pt->element).x;
					pt = pt->next;
				} while (pt != S.firstNode);
			}
		}
		else {   //S内的点不共线,形成凸包
			pointInFlat X;  //确定S内部的一个点X
			X.x = (x1 + x2 + x) / 3;
			X.y = (y1 + y2 + y) / 3;
			for (int k =0; k <2; ++k)
			{
				//采用冒泡排序对S中的点进行排序
				chainNode<pointInFlat>*pr, *pd, *pb, *ps,*p;
				pr = S.lastNode;
				pd =S.firstNode;    //pd = NULL;
	            
				bool sorted = false;
				do{
					sorted = true;
					ps = pr->next;                         //由于X在计算机中的计算结果和实际有偏差,故实际相等的点会改变原来点的顺序,不过对结果没影响
					while (ps->next != pd)     //比较后面的元素
					{
						pb = ps->next;
						float xs = ps->element.x;
						float ys = ps->element.y;
						float xb = pb->element.x;
						float yb = pb->element.y;
						float lA_s= 0;
						float lA_b= 0;
						if (k == 0)
						{
							 lA_s = sqrt(pow(xs - X.x, 2) + pow(ys - X.y, 2));
							 lA_b = sqrt(pow(xb - X.x, 2) + pow(yb - X.y, 2));//计算开始的两个点到X的距离,进行比较
						}
						else
						{
						 float txs = X.y - ys;
						 float tys = xs - X.x;
						       lA_s = asin(tys / sqrt(txs*txs + tys*tys));
					           lA_s= lA_s*180/PI; 
						    if (lA_s < 0 && txs>0)                           //角在第四象限,需要转化
							 lA_s += 360;
					      else if (txs<0)                                   //角在第二三象限,需要转化
						       lA_s=180 - lA_s;

							float txb = X.y - yb;
							float tyb = xb - X.x;
							      lA_b = asin(tyb / sqrt(txb*txb + tyb*tyb));
							      lA_b = lA_b*180/PI;
						       if (lA_b<0 && txb>0)
							      lA_b+=360;
							    else if (txb<0)
								lA_b= 180 - lA_b;
					}
						if (lA_s>lA_b)                                         //交换指针域,速度要比交换数据域快
						{
							pr->next = pb;
							pb->pre = pr;
							ps->next = pb->next;
							pb->next->pre = ps;
							pb->next = ps;
							ps->pre = pb;
							sorted = false;
							if (pr == S.lastNode)                               //在交换时要特别注意首和尾节点,首节点和尾节点参与交换后,要说明尾节点和头节点
							{
								S.firstNode = pb;
								pb->pre = S.lastNode;
							}
							if (pb == S.lastNode)                            
							{
								S.lastNode = ps;
								ps->next = S.firstNode;
							}
						}
						pr = pr->next;
						ps = pr->next;
						pd = S.firstNode;
					}
					pd = ps;
					pr = S.lastNode;
				} while (pr->next != pd && !sorted);
			}
			//删除非极点的点
			//首先找到y最小的点
			pointInFlat ymin;
			chainNode<pointInFlat>*p_ym,*deleteNode;//记录y最小的指针
			chainNode<pointInFlat>*px,*prx,*prrx;//px为当前点的指针,prx为逆时针第二个指针,prrx为第三个指针
			chainNode<pointInFlat>*pi = S.firstNode;
			ymin.y = (pi->element).y;
			p_ym = S.firstNode;
			do 
			{
				if (ymin.y>(pi->element).y)
					p_ym= pi;
					pi = pi->next;
			} while (pi->next != S.firstNode);

			for (px = p_ym,prx=px->next; prx!=p_ym;)   //删除非极点的点,for循环的第一部分是起始值,第二部分是判断
			{
				prrx = prx->next;
				//如果x,rx,rrx的逆时针夹角小于或等于180度,则删除rx. 向量叉乘积结果可以判断是顺时针还是逆时针。这里要把向量做三维处理才可以
				auto ax = (prx->element).x - (px->element).x;
				auto ay = (prx->element).y - (px->element).y;    //建立向量a
				auto bx= (prrx->element).x - (prx->element).x;
				auto by = (prrx->element).y - (prx->element).y;    //建立向量b
				if (ax*by -bx*ay <=0)   //条件进行转化
				{
					deleteNode = prx;
					prx = px;
					px = prx->pre; //开始的两节点都后退
					delete deleteNode;
					prx->next = prrx;
					prrx->pre = prx;             
					//delete deleteNode;
				}
				 px = prx; //如果节点方向是左转的,则继续前进。
				 prx = prrx; 
			}

		for (chainNode<pointInFlat>*p = S.firstNode; p != S.lastNode; p = p->next)
				cout << (p->element).x << " " << (p->element).y << "   ";
			cout << (S.lastNode->element).x << " " << (S.lastNode->element).y << "  ";
		}
	}
int main()
{
	doublychain<pointInFlat>S;
	int i = 0;
	ifstream infile("coordinate.txt");   //把txt文件转换为输入流
	assert(infile.is_open());            //检查文件是否打开
	string str;                          //逐行读入的中间变量
	while (getline(infile, str))         //逐行读入
	{
		pointInFlat po;
		istringstream istr(str);         //将string分割为单个单词,相当于从string中读取数据
		istr>>po.x>>po.y;
		S.insert(i, po);
		++i;
	}
	GeConvexHull(S);
	return 0;
}
测试结果:
txt输入坐标:
0  0
0  1
1 1
2  1
2  0
1 -1
1 0
1 3
输出坐标:
1 -1   2 0   2 1   1 3   0 1   0 0
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值