📜  算法|图最小生成树|问题6(1)

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

算法 | 图最小生成树 | 问题6

在图论中,最小生成树(Minimum Spanning Tree)是一个连通图的一个子图,它包含图中的所有顶点,但是没有任何的环,同时其所有边的权值和最小。最小生成树问题通常是指在一个加权连通图中找到一棵权值最小的生成树。

算法实现

常见的最小生成树算法有Prim算法和Kruskal算法。

Prim算法

Prim算法是一种贪心算法,它的基本思路是选定一个顶点作为起点,然后根据贪心策略,在与已选择的点相邻的所有边中选择一个最小的边,然后将其与已选择的点形成一条关联,重复上述过程,直到所有节点都被选择。

import heapq

def prim(graph, start):
    # 初始化visited集合和dist字典
    visited = set()
    dist = {start:0}
    for vertex in graph:
        if vertex != start:
            dist[vertex] = float('inf')
    # 将起点加入最小堆中
    heap = [(0, start)]
 
    while heap:
        # 取最小堆中的最小边
        (weight, current_vertex) = heapq.heappop(heap)
        if current_vertex not in visited:
            visited.add(current_vertex)
            # 遍历当前点的所有边
            for neighbor, edge_weight in graph[current_vertex].items():
                # 若相邻点没有被访问
                if neighbor not in visited:
                    # 如果找到了更短的距离,更新dist字典
                    if edge_weight < dist[neighbor]:
                        dist[neighbor] = edge_weight
                        # 将相邻点和权值入堆
                        heapq.heappush(heap, (edge_weight, neighbor))
    return dist
Kruskal算法

Kruskal算法也是一种贪心算法,它寻找图中最小生成树的过程中,每次将一条边加入集合中,如果这条边导致集合中的边形成了环,则将该边舍弃。重复这个过程,直到加入n-1条边。

def kruskal(graph):
    # 初始化所有节点的祖先为自己
    ancestors = {node: node for node in graph.keys()}
    # 初始化边的集合
    edges = []
 
    for node in graph:
        for neighbor, edge_weight in graph[node].items():
            # 将边按权值从小到大排序
            edges.append((edge_weight,node,neighbor))
    edges.sort()
 
    # 初始化最小生成树的边集合
    mst_edges = set()
    for edge in edges:
        weight, node, neighbor = edge
        # 如果这条边形成了环,则舍弃这条边
        if find(node, ancestors) != find(neighbor, ancestors):
            # 将两个节点的祖先设为同一节点,表示它们已经是一个联通块
            union(node, neighbor, ancestors)
            mst_edges.add(edge)
 
    # 初始化权值和为0
    weight_sum = 0
    for edge in mst_edges:
        weight_sum += edge[0]
 
    return weight_sum
      
def union(node, neighbor, ancestors):
    ancestors[find(node,ancestors)] = find(neighbor,ancestors)
 
def find(node, ancestors):
    while node != ancestors[node]:
        node = ancestors[node]
    return node
示例
graph = {
    'A': {'B': 1, 'C': 2},
    'B': {'A': 1, 'C': 3, 'D': 4},
    'C': {'A': 2, 'B': 3, 'D': 5},
    'D': {'B': 4, 'C': 5}
}

print("Prim算法:", prim(graph, 'A'))
print("Kruskal算法:", kruskal(graph))

输出:

Prim算法: {'A': 0, 'B': 1, 'C': 2, 'D': 4}
Kruskal算法: 7
总结

最小生成树问题是图论中的一个重要问题,Prim算法和Kruskal算法都可以解决该问题,具体选用哪种算法取决于具体情况。在实现算法时,需要注意细节,例如如何表示图、如何处理已选择的节点等。