Python实现最小生成树-Prim和Kruskal算法

1. Prim算法

Prim算法用于找到一个连通加权无向图的最小生成树。它的基本思想是从一个节点开始,每次选择边权最小且不会形成环的一条边,直到包括图中所有的节点。

1.1 算法步骤:
  1. 从任意节点开始,初始化一个节点集合,将其标记为已访问。
  2. 找到所有连接已访问节点和未访问节点的边,选择权重最小的边,并将其连接的未访问节点加入集合。
  3. 重复步骤2,直到所有节点都被访问。
1.2 Python实现Prim算法
import heapq

def prim(graph):
    # 初始化最小生成树和总权重
    mst = []
    total_weight = 0
    # 从第一个节点开始
    start_node = list(graph.keys())[0]
    # 使用优先队列存储边
    edges = [(0, start_node, start_node)]
    visited = set()
    
    while edges:
        weight, u, v = heapq.heappop(edges)
        if v in visited:
            continue
        visited.add(v)
        mst.append((u, v, weight))
        total_weight += weight
        
        for next_node, next_weight in graph[v].items():
            if next_node not in visited:
                heapq.heappush(edges, (next_weight, v, next_node))
    
    return mst, total_weight

# 示例图表示为邻接表
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

mst, total_weight = prim(graph)
print("最小生成树:", mst)
print("总权重:", total_weight)

2. Kruskal算法

Kruskal算法用于找到一个连通加权无向图的最小生成树。它的基本思想是每次选择权重最小的边,如果这条边的加入不会形成环,则将其加入最小生成树中,直到包括图中所有的节点。

2.1 算法步骤:
  1. 将所有边按权重从小到大排序。
  2. 初始化一个并查集(Union-Find)来记录每个节点所属的集合。
  3. 按权重从小到大选择边,如果边连接的两个节点不属于同一个集合,则将其加入最小生成树,并合并这两个节点的集合。
  4. 重复步骤3,直到最小生成树包含所有节点。
2.2 Python实现Kruskal算法
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
    
    def find(self, u):
        if self.parent[u] != u:
            self.parent[u] = self.find(self.parent[u])
        return self.parent[u]
    
    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            if self.rank[root_u] > self.rank[root_v]:
                self.parent[root_v] = root_u
            elif self.rank[root_u] < self.rank[root_v]:
                self.parent[root_u] = root_v
            else:
                self.parent[root_v] = root_u
                self.rank[root_u] += 1

def kruskal(graph):
    # 将边按权重排序
    edges = sorted(graph['edges'], key=lambda edge: edge[2])
    # 初始化并查集
    uf = UnionFind(graph['num_nodes'])
    mst = []
    total_weight = 0
    
    for u, v, weight in edges:
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            mst.append((u, v, weight))
            total_weight += weight
    
    return mst, total_weight

# 示例图表示为边列表
graph = {
    'num_nodes': 4,
    'edges': [
        (0, 1, 1),
        (0, 2, 4),
        (1, 2, 2),
        (1, 3, 5),
        (2, 3, 1)
    ]
}

mst, total_weight = kruskal(graph)
print("最小生成树:", mst)
print("总权重:", total_weight)

这两个算法分别解决了找到连通加权无向图的最小生成树的问题。Prim算法适用于稠密图,因为它主要操作节点;Kruskal算法则适用于稀疏图,因为它主要操作边。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Prim算法实现: ```Python from heapq import heappush, heappop # 堆优化 from collections import defaultdict def prim(graph, start): mst = defaultdict(set) # 存储最小生成树的结果 visited = set([start]) # 已经访问过的节点 edges = [ (cost, start, to) for to, cost in graph[start].items() ] # 从起始节点开始的所有边 # 将边按照权值排序 edges.sort() # 遍历每一条边 while edges: cost, frm, to = heappop(edges) # 取出当前最小的边 if to not in visited: # 如果该节点未访问过,加入最小生成树 visited.add(to) mst[frm].add(to) for to_next, cost in graph[to].items(): # 将新加入的节点的边加入边集中 if to_next not in visited: heappush(edges, (cost, to, to_next)) return mst ``` Kruskal算法实现: ```Python def kruskal(graph): mst = defaultdict(set) # 存储最小生成树的结果 vertices = set(graph.keys()) # 所有节点 edges = [ (graph[v1][v2], v1, v2) for v1 in vertices for v2 in graph[v1] ] # 所有边 # 将边按照权值排序 edges.sort() # 遍历每一条边 for cost, frm, to in edges: if to not in vertices: # 如果该边的终点不在当前最小生成树中,加入该边 continue vertices.remove(to) mst[frm].add(to) mst[to].add(frm) return mst ``` 其中,图的数据结构可以使用字典来表示,例如: ```Python graph = { 'A': {'B': 2, 'D': 6}, 'B': {'A': 2, 'C': 3, 'D': 8}, 'C': {'B': 3}, 'D': {'A': 6, 'B': 8}, } ``` 其中,每个键(例如'A')表示一个节点,对应的值(例如{'B': 2, 'D': 6})表示与该节点相连的边及其权值。例如,图中节点'A'与节点'B'之间的边权为2,节点'A'与节点'D'之间的边权为6。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲人编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值