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

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

凸包 & 单调链算法

什么是凸包?

凸包是一个凸多边形,是这个多边形凸包含所有点且所有点都在凸包内部。类比于一个面包,外层是面包皮,里面是馅料,面包皮就相当于凸包。

求凸包的算法

计算凸包算法非常多,最常见的一般有:Graham算法,Jarvis算法,Andrew算法,QuickHull算法,Divide And Conquer算法等等。

这里介绍其中一种非常常见、简单易懂的算法:单调链算法。

单调链算法

单调链算法是一种时间复杂度为 $O(n \log n)$ 的求凸包算法,在实际中比较常用。单调链算法的思想是:先按照 $x$ 坐标排序,然后把点分成两部分:上凸壳和下凸壳。对于两个凸壳的求解过程是类似的,只是在某些过程中,比较方式不同而已。

算法流程
  1. 对所有点按照x轴排序,找出最左边和最右边的点,分别作为左右边框上的点,分别命名为 Oleft, ORight

  2. 扫描全部点,将他们全部放置在两条链表上,第一个点放在第一条链表上,然后顺序处理每个点,如果是逆时针旋转,插入左侧链表,否则插入右侧链表。

  3. 合并上下凸壳,左右两个不相交的单调链合并为一个单调链。在这个过程中,为了确定下凸壳需要退栈操作。

  4. 输出凸包上的所有点。

代码实现

按照上述思路,这里给出单调链算法的Python实现。

import functools

# 判断两个向量的角度大小,小于180度视为逆时针,大于180度视为顺时针
def cross(u, v):
    return u[0]*v[1] - u[1]*v[0]

def cmp(a, b):
    # 比较两个点的位置关系,用于排序
    if a[0] != b[0]:
        return a[0] - b[0]
    return a[1] - b[1]

# 计算凸包的主函数
def convex_hull(points):
    # 对点按照x坐标排序,并找到左右两个边界点
    left = functools.reduce(lambda x, y: x if x[0] < y[0] else y, points)
    right = functools.reduce(lambda x, y: x if x[0] > y[0] else y, points)
    
    # 根据点集排序规则进行排序
    sorted_points = sorted(points, key=functools.cmp_to_key(cmp))
    
    # 初始化上、下凸壳
    top, bottom = [left], [left]

    # 处理剩余点
    for p in sorted_points[1:]:              
        # 如果是逆时针旋转,插入到上凸壳中
        if cross((p[0]-top[-1][0], p[1]-top[-1][1]), (top[-1][0]-top[-2][0], top[-1][1]-top[-2][1])) > 0:
            while len(top) > 1 and cross((p[0]-top[-1][0], p[1]-top[-1][1]), (top[-1][0]-top[-2][0], top[-1][1]-top[-2][1])) > 0:
                top.pop()
            top.append(p)
            
        # 如果是顺时针旋转,插入到下凸壳中
        if cross((p[0]-bottom[-1][0], p[1]-bottom[-1][1]), (bottom[-1][0]-bottom[-2][0], bottom[-1][1]-bottom[-2][1])) < 0:
            while len(bottom) > 1 and cross((p[0]-bottom[-1][0], p[1]-bottom[-1][1]), (bottom[-1][0]-bottom[-2][0], bottom[-1][1]-bottom[-2][1])) < 0:
                bottom.pop()
            bottom.append(p)

    # 合并上下凸壳
    hull = top + bottom[1:-1][::-1]
    
    # 返回凸包上的点
    return hull
参考资料