📜  凸包 |设置 1(Jarvis 的算法或包装)(1)

📅  最后修改于: 2023-12-03 14:50:10.216000             🧑  作者: Mango

凸包 |设置 1(Jarvis 的算法或包装)

概述

凸包是指包含给定点集的最小凸多边形或凸壳。其中最小凸多边形是指包含所有点的最小凸多边形,而凸壳则是指点集上所有点的凸包。Jarvis算法也称作包裹算法或礼物包装算法,是一种朴素的求解凸包的算法。

算法思路

Jarvis算法的基本思路是从所有点集中选出一个左侧最靠近的点,然后逆时针扫描,每次选取下一个逆时针方向最接近的点加入凸包中。最终凸包构成的是所有点集中最小的凸多边形。

具体来说,该算法的核心过程分为三步:

  1. 找到左侧最靠近的点(设其为P);
  2. 枚举点集中的其他点,找到以P为起点的所有有向直线中最靠左的那个点(设其为nextP);
  3. 将nextP加入凸包中,并将其设为下一个起点。

接着,不断执行此过程直到回到起点。

代码实现

以下代码是使用Java语言实现的Jarvis算法。

public static int cross(point a, point b, point c) {
    return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

public static double dis(point a, point b) {
    return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

public static point next(point p, ArrayList<point> lst) {
    point nextP = lst.get(0);
    for (int i = 1; i < lst.size(); i++) {
        if (lst.get(i).y < nextP.y || (lst.get(i).y == nextP.y && lst.get(i).x < nextP.x)) {
            nextP = lst.get(i);
        }
    }
    return nextP;
}

public static ArrayList<point> Jarvis(ArrayList<point> lst) {
    if (lst.size() <= 2) {
        return lst;
    }

    point startP = lst.get(0);
    for (int i = 1; i < lst.size(); i++) {
        if (lst.get(i).x < startP.x || (lst.get(i).x == startP.x && lst.get(i).y < startP.y)) {
            startP = lst.get(i);
        }
    }

    ArrayList<point> res = new ArrayList<>();
    res.add(startP);
    point pre = startP;
    while (true) {
        point nextP = next(pre, lst);
        if (nextP == startP) {
            break;
        }
        res.add(nextP);
        pre = nextP;
    }

    return res;
}

其中,point是个简单的二维点类型,具有x,y两个成员变量。

class point {
    int x, y;

    point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
总结

Jarvis算法虽然过程朴素,但实际应用广泛,尤其是对于小规模数据,其表现可以和更复杂的算法相当甚至超过。对于大规模数据,该算法的表现会有所下降,可以使用更高效的算法,如Graham扫描或快速凸包。

参考资料