📜  找到最小生成树的权重(1)

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

找到最小生成树的权重

什么是最小生成树?

最小生成树,是指一张给定无向图的一棵生成树,并且其边权和最小。生成树,就是一棵包含了给定图中全部n个顶点和只有n-1条边的树。

如何找到最小生成树的权重?

常用的有两种算法:Prim算法和Kruskal算法。

Prim算法
  1. 首先任意选择一个顶点(比如第一个顶点)作为起始顶点,然后将该顶点加入到已经访问的顶点集合中,并将其标记为已访问;
  2. 接着,将该顶点与其它还未访问的顶点进行连线,并将连线的边权值记录下来;
  3. 从所有的边权值中选出最小的一条边权值,将其所连接的未访问过的顶点标记为已访问,并加入到已访问的顶点集合中;
  4. 重复执行第2和第3步,直到图中的全部顶点都被访问过。
# Prim算法实现
def prim(graph):
    v_num = len(graph)                     # 记录图中顶点个数
    mst = [None] * v_num                    # 用于保存最小生成树的边
    values = [999999] * v_num               # 用于保存每个顶点到最小生成树的最短边的边权值
    visited = [False] * v_num               # 标记每个顶点是否已经被访问过
    visited[0] = True                       # 从任意一个顶点开始都可以
    for i in range(v_num - 1):
        min_value = 999999                  # 记录到最小生成树的最短边的权重值
        for j in range(v_num):
            if visited[j]:                  # 已访问过该顶点,则查找它的邻居顶点
                for k in range(v_num):
                    if not visited[k] and graph[j][k] < values[k]:   # 找到一条最短边
                        values[k] = graph[j][k]
                        mst[k] = (j, k, graph[j][k])
                        if graph[j][k] < min_value:
                            min_value = graph[j][k]
                            next_node = k
        visited[next_node] = True
    return mst
Kruskal算法
  1. 将无向图中的所有边按权值从小到大排序;
  2. 选择最小的一条边并将它的两个端点所在的连通块合并成一个连通块(如果一开始就在同一个连通块中则不做任何操作),直到所有顶点都在同一个连通块中为止。
# Kruskal算法实现
def kruskal(graph):
    v_num = len(graph)                 # 记录图中顶点个数
    edges = []
    for i in range(v_num):
        for j in range(i, v_num):       # 无向图,只需要遍历一半就可以了
            if graph[i][j] != 0:
                edges.append((i, j, graph[i][j]))
    edges.sort(key=lambda x: x[2])      # 按边权值从小到大排序
    parents = list(range(v_num))        # 用于记录每个顶点的祖先节点
    mst = []
    for edge in edges:
        v1, v2, weight = edge
        ancestor1, ancestor2 = find(parents, v1), find(parents, v2)
        if ancestor1 != ancestor2:      # 如果两个顶点不在同一个连通块中,则将它们连接起来
            mst.append(edge)
            parents[ancestor1] = ancestor2    # 将连接点的祖先节点更新
    return mst

def find(parents, v):
    """
    查找顶点v的祖先节点
    """
    if parents[v] != v:
        parents[v] = find(parents, parents[v])
    return parents[v]
怎样选择算法?

Prim算法和Kruskal算法在实现上略有不同,但它们的时间复杂度都为O(Elog⁡E),其中 E 表示边的数量,因此速度上没有太大的差别。选择哪种算法更多取决于具体问题的特点和实现难易程度。