凸包算法
凸包的概念:
在某个二维平面上的给定一个点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
凸包算法
从点集中获取凸包的方法比较常用的有Jarvis步进法,Graham扫描法和Andrew算法,在时间性能(速度)上,Jarvis步进法<Graham扫描法<Andrew算法。
Jarvis步进法
凸包之Jarvis步进法_H煊的博客-CSDN博客_jarvis步进法
Graham扫描法
//点 struct Point { float X, Y; Point() :X(0.0f), Y(0.0f){} Point(float x, float y) : X(x), Y(y){} bool operator==(const Point& other) { return (abs(X - other.X) + abs(Y - other.Y)) < 0.00001f; } bool operator!=(const Point& other) { return !((*this) == other); } friend std::ostream& operator<<(std::ostream& out, const Point& p) { out << '(' << p.X << ", " << p.Y << ')'; return out; } }; //Graham扫描法 class ConvexHullAlgorithm { //根据原点base,求target点的极坐标的角度的cos值 //由于base的y值是最小值,所以角度angle(target)区间是[0,180], //且val = cos(angle(target))在[0,180]→[-1,1]是一一映射的。 //因此可以用val代替angle(target) float CalPolarAngle(const Point& base, const Point& target) const { float xDis = target.X - base.X; float yDis = target.Y - base.Y; float dis = std::sqrtf(xDis * xDis + yDis * yDis); return xDis / dis; } //只是为了方便排序而设的结构 struct PolarPoint { float polarAngle; Point point; PolarPoint():polarAngle(0.0f),point(0.0f,0.0f){} }; //利用叉乘判断a->b->c是否逆时针排序 bool IsAnticlockwise(const Point& a, const Point& b, const Point& c) const { Point ab(b.X - a.X, b.Y - a.Y); Point bc(c.X - b.X, c.Y - b.Y); return ab.X * bc.Y - ab.Y * bc.X > 0; } public: //公开接口:获取凸包 std::vector<Point> ConvexHull(std::vector<Point>& points) { int size = points.size(); if (size <= 3) return points; std::vector<PolarPoint> pps(size); //找出y值最小的点base auto minit = std::min_element(points.begin(), points.end(), [](const Point& lhs, const Point& rhs) {return lhs.Y < rhs.Y; }); std::swap(*minit, *points.begin()); Point base = points[0]; //计算基于base为原点的其他每个点极点坐标角度的cos值 for (int i = 1; i < size; ++i) { pps[i].point = points[i]; pps[i].polarAngle = CalPolarAngle(base, points[i]); } //根据极点坐标角度的cos值进行排序 std::sort(pps.begin() + 1, pps.end(), [](const PolarPoint& lhs, const PolarPoint& rhs) {return lhs.polarAngle > rhs.polarAngle; }); std::vector<Point> ans; ans.push_back(pps[0].point); ans.push_back(pps[1].point); int num = 1; //找出凸包 for (int i = 2; i < size; ++i) { while(num > 0 && !IsAnticlockwise(ans[num - 1], ans[num], pps[i].point)) { ans.pop_back(); --num; } ans.push_back(pps[i].point); ++num; } return ans; } };
Andrew算法
Andrew算法是Graham扫描法的改进法,待我再研究一下。