最小生成树是指带权无向图中,其各边权值和最小的生成树。这个问题在日常生活中会广泛遇到,如何用最小的代价把网络中各点连接起来。
常用的算法有Kruskal,Prim,我们对这两个典型算法进行Python实现。
Kruskal
Kruskal算法基于简单连通分量的最小代价互联。将初始图G中各边按权值从小到大排列成列表edges,存储方式为 (weight, vi, vj),每次取出一条边,检查其连接的两端是否已连通,若尚未连通则将该边加入生成树,并修改该边所连接的两个连通分量的状态,否则删除该边。相应的Python代码实现如下:
1 def Kruskal(graph):
2 vnum = graph.vertex_num() #得到图中点的个数
3 mst, edges = [], []
4 reps = [i for i in range(vnum)] #初始化代表元
5 for vi in range(vnum): #收集各边
6 for vj,weight in graph.out_edges(vi):
7 edges.append((weight,vi,vj))
8 edges.sort() #将边按权值weight从小到大排序
9 for weight, vi, vj in edges: #逐个遍历边,将其加入到mst中
10 if reps[vi] != reps[vj]:
11 mst.append((vi, vj, weight))
12 repi, repj = reps[i], reps[j]
13 for v in range(vnum): #更新代表元
14 if reps[v] == repj:
15 reps[v] = repi
16 return mst
Prim
Prim算法是基于所谓的MST准则,将图的点集分为两部分,mst和V,依次将边顶点分属于两个点集的最小权值边加入到生成树中,同时将V中连接的点加入到mst中,相比于Kruskal不断将最小权值边加入生成树,Prim则是连续扩大最小生成树中的点集。
相应的Python代码实现如下:
1 def Prim(graph):
2 vnum = graph.vertex_num()
3 edges = PrioQueue((0,0,0)) #每次将新边加入到一个优先队列中
4 mst = [None] * vnum #用于判断边所连接的点是否已经遍历过
5 edge_count = 0
6 while edge_count < vnum and not edges.is_empty():
7 weight, vi, vj = edges.dequeue()
8 if mst[vj] == None:
9 edge_count += 1
10 mst[vj] = (vi, weight)
11 for i,w in graph.out_edges(vj): #将新点的出边加入优先队列
12 if not mst[i]:
13 edges.enqueue((w, vj, i))
14 return mst