最小生成树是指带权无向图中,其各边权值和最小的生成树。这个问题在日常生活中会广泛遇到,如何用最小的代价把网络中各点连接起来。

常用的算法有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