凸包算法

凸包的概念:

在某个二维平面上的给定一个点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。

凸包算法

从点集中获取凸包的方法比较常用的有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扫描法的改进法,待我再研究一下。

 

posted @ 2022-09-23 09:52  mshentai  阅读(116)  评论(0编辑  收藏  举报