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;
}