📜  凸包|单调链算法(1)

📅  最后修改于: 2023-12-03 15:22:37.246000             🧑  作者: Mango

凸包|单调链算法

凸包是计算几何中的一个重要问题,它求得是一个点集最小凸多边形的顶点集合。而单调链算法则是求解凸包的一种重要算法。

原理

凸包可以使用暴力枚举和 Graham-Scan 等算法实现,但是单调链算法是比较高效的一种方法。它的原理如下:

  1. 首先对点集按照 x 坐标进行排序;

  2. 分别求出从左到右和从右到左的两个单调链;

  3. 将这两个单调链合并成一个。

对于单调链的求解,分别有两个单调栈:

  1. 左单调栈:从左到右不断向栈中添加点,保证栈中点纵坐标单调递增;

  2. 右单调栈:从右到左不断向栈中添加点,保证栈中点纵坐标单调递减。

实现

下面是单调链算法的具体实现:

def monotonic_chain(points):
    # 对点按照 x 坐标排序
    points.sort()

    # 从左到右的单调栈
    left_stk = []
    for pt in points:
        while len(left_stk) >= 2 and (left_stk[-1] - left_stk[-2]).cross(pt - left_stk[-2]) < 0:
            left_stk.pop()
        left_stk.append(pt)

    # 从右到左的单调栈
    right_stk = []
    for pt in reversed(points):
        while len(right_stk) >= 2 and (right_stk[-1] - right_stk[-2]).cross(pt - right_stk[-2]) < 0:
            right_stk.pop()
        right_stk.append(pt)

    # 合并两个单调链
    return left_stk[:-1] + right_stk[:-1]

其中 cross 函数即向量叉积,定义如下:

class Vec2:
    # vector2 class
    ...
    
    def cross(self, other):
        return self.x * other.y - self.y * other.x
使用

使用单调链算法求解凸包可以得到一个点集的最小凸包顶点集合。

下面是一个例子:

points = [Vec2(0, 3), Vec2(2, 2), Vec2(1, 1), Vec2(2, 1),
          Vec2(3, 0), Vec2(0, 0), Vec2(3, 3)]
hull_pts = monotonic_chain(points)

hull_pts 将得到点集的最小凸包顶点集合。

结论

在计算几何中,凸包问题很重要,而单调链算法是其中的一种高效算法。实际上,在计算几何中还有其他的算法,例如 Graham-Scan、QuickHull、Chan 等,不仅效率高,而且更加通用,可以处理各种凸包问题。