最小凸包

1. 硬币法,存在问题:按顺时针排序,如果Pn, Pm, pP0共线如何处理?(比如{0, 10}, {5, 0}, {10, 0}三点凸包如何处理,{5, 0}是不是凸包上的点)

#include <iostream>
#include <vector>
#include <algorithm>

struct POINT
{
	double x;
	double y;
};
typedef POINT VECTOR;

class
{
public:
	//携带参数,极限点
	void CarryParams(double _x, double _y)
	{
		bx = _x;
		by = _y;
	}
	bool operator()(const POINT _p1, const POINT _p2) const
	{
		VECTOR v01 = {_p1.x - bx, _p1.y - by};
		VECTOR v02 = {_p2.x - bx, _p2.y - by};
		double cos01 = v01.x * 1 / (sqrt(pow(v01.x, 2) + pow(v01.y, 2)) * 1);
		double cos02 = v02.x * 1 / (sqrt(pow(v02.x, 2) + pow(v02.y, 2)) * 1);
		return cos01 < cos02;
	}
private:
	double bx;
	double by;
}AngleSorter;//顺时针角度排序的二元谓词函数对象
bool isRight(const POINT & p1/*后点*/, const POINT & p2/*中点*/, const POINT & p3/*前点*/)
{
	VECTOR p12_90 = {p2.y - p1.y, -(p2.x - p1.x)};//向量p1 p2顺时针旋转90°
	VECTOR p23 = {p3.x - p2.x, p3.y - p2.y};
	return p12_90.x * p23.x + p12_90.y * p23.y >= 0;
}

//创建凸包
POINT * CreateHull(const POINT * _pts, int _n/*点群点数*/, int * _hull_size/*凸包点数*/)
{
	using namespace std;
	double y_min = _pts[0].y;
	int y_min_index = 0;
	for(int i = 0; i < _n; i++)
	{
		if(_pts[i].y < y_min)
		{
			y_min = _pts[i].y;
			y_min_index = i;
		}
	}
	vector<POINT> hull;
	for(int i = 0; i < _n; i++)
	{
		if(i != y_min_index)
		{
			hull.push_back(_pts[i]);
		}
	}//至此得到(_n - 1)个元素的hull
	//比较p0与hull中的(n - 1)个点构成的矢量和x正向基矢cos值,从小大到排序
	AngleSorter.CarryParams(_pts[y_min_index].x, _pts[y_min_index].y);
	sort(hull.begin(), hull.end(), AngleSorter);
	//头尾都插入_pts[i],得到(_n + 1)个元素的hull
	hull.insert(hull.begin(), _pts[y_min_index]);
	hull.push_back(_pts[y_min_index]);//至此顺时针排序并尾接起始值,已测试,正确无误(所谓顺时针排序其实有bug,自己体会)

	vector<POINT>::iterator midle_coin;
	for(midle_coin = hull.begin() + 1; true; midle_coin++)
	{
		while(!isRight(*(midle_coin - 1), *(midle_coin), *(midle_coin + 1))/*判断不是右拐*/)
		{
			midle_coin = hull.erase(midle_coin) - 1;//删除中间硬币对应点
		}
		if(midle_coin + 1 == hull.end() - 1)
		{
			break;
		}
	}
	POINT * pRst = new POINT [hull.size() - 1];//不包括尾接的起始点
	*_hull_size = hull.size() - 1;
	for(int i = 0; i < hull.size() - 1; i++)
	{
		pRst[i] = hull[i];
	}
	return pRst;
}
//摧毁凸包
void DestroyHull(POINT * _pts)
{
	delete [] _pts;
	_pts = NULL;
}
int main()
{
	POINT pts[6] = {{1, 0}, {2, 8}, {3, 5}, {5, 4}, {8, 9}, {9, 0}};
	int n;
	POINT * p_result = ::CreateHull(pts, 6, &n);
	for(int i = 0; i < n; i++)
	{
		std::cout<<"("<<p_result[i].x<<", "<<p_result[i].y<<")"<<std::endl;
	}
	::DestroyHull(p_result);
	return 0;
}


2. 找最大角法,低效

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

struct Point
{
	double x;
	double y;
};

struct Vector
{
	double vx;
	double vy;
};

bool operator!=(const Point & p1, const Point & p2)
{
	if(p1.x == p2.x && p1.y == p2.y)
		return false;
	else
		return true;
}

bool findbottom(const Point & p1, const Point & p2)
{
	if(p1.y<p2.y)
		return true;
	else
		return false;
}

typedef std::vector<Point> Points_vec;

Point * CreateHull(const Point * _points/*原始顶点数组地址*/, int _source_count/*原始定点数目,必须大于2*/, int * _result_count/*用于记录凸包顶点个数的int变量地址*/)
{
	Points_vec all_points(_points,_points+_source_count);//存储全部顶点的容器
	Vector last_vec = {1,0};//记录从凸包最后一个点指向前一个顶点的向量,初始向量方向为x轴正向

	Point temp_pt = *std::min_element(all_points.begin(),all_points.end(),findbottom);//寻找最低点

	Points_vec convex_hull;//存储凸包顶点的容器
	convex_hull.push_back(temp_pt);
	
	do
	{
		double min_cos = 1.0;//cos值越小,夹角越大
		Vector next_vec;

		//找下一个temp_pt
		for(int i = 0;i<all_points.size();i++)
		{	
			Vector t_vec = {all_points[i].x - convex_hull.back().x, all_points[i].y - convex_hull.back().y};
			//若夹角更大
			double t_cos = ((t_vec.vx * last_vec.vx) + (t_vec.vy * last_vec.vy))/(sqrt(last_vec.vx * last_vec.vx + last_vec.vy * last_vec.vy) * sqrt(t_vec.vx * t_vec.vx + t_vec.vy * t_vec.vy));
			if(t_cos<min_cos)
			{
				temp_pt = all_points[i];
				next_vec = t_vec;
				min_cos = t_cos;
			}
		}//在余点中找到适合的点
		//找到点后添加到convex_hull中,last_vec刷新并反向
		convex_hull.push_back(temp_pt);
		last_vec.vx = -next_vec.vx;
		last_vec.vy = -next_vec.vy;
	}
	while(temp_pt != convex_hull.front());
	//删掉最后和第一个点重复的点
	convex_hull.pop_back();
	
	*_result_count = convex_hull.size();
	Point * hull = new Point[convex_hull.size()];
	std::copy(convex_hull.begin(),convex_hull.end(),hull);
	return hull;
}

//样本数据
Point points[8] = {{1.2,3},{8,3},{-1.5,2},{3,2.5},{-0.5,1},{9,1.7},{1,-4},{6,-2}};

int main(void)
{
	std::cout<<"点集:"<<std::endl;
	for(int i=0;i<8;i++)
	{
		std::cout<<"("<<points[i].x<<","<<points[i].y<<")"<<std::endl;
	}
	std::cout<<std::endl;
	int num;
	Point * collection = CreateHull(points,8,&num);
	std::cout<<"凸包顶点:"<<std::endl;
	for(int i = 0; i < num; i++)
	{
		std::cout<<"("<<collection[i].x<<","<<collection[i].y<<")"<<std::endl;
	}
	delete [] collection;

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值