Kruskal算法
Kruskal算法是一种构造最小生成树的简单算法,其中的思想比较简单。
基本思想
设G=(V,E)是一个网络,其中|V|=n。Kruskal算法构造最小生成树的过程是:
- 初始时取包含G中所有n个顶点但没有任何边的孤立点子图T=(V,{}),T里的每个顶点自成一个连通分量。下面将通过不断扩充T的方式构造G的最小生成树。
- 将边集E中的边按权值递增的顺序排序,在构造中的每一步顺序地检查这个边序列,找到下一条(最短的)两端点位于T的两个不同连通分量的边e,把e加人T。这导致两个连通分量由于边e的连接而变成了一个连通分量。
- 每次操作使T减少一个连通分量。不断重复这个动作加人新边,直到T中所有顶点都包含在一个连通分量里为止,这个连通分量就是G的一棵最小生成树。
可以先用一个优先队列存储所有的边,这样可保证每次取到的都是最短边。为每个连通分量确定一个代表元,如果两个顶点的代表元相同,它们就互相连通,属于同一连通分量。。加人一条边减少了连通分量,这时需要选一个顶点,让被合并的两个连通分量里的顶点都以它为代表元。完成这件事的简单方法是从原来的两个代表元中任选一个,而后更新另一连通分量中顶点的代表元。
算法的python实现
下面给出Kruskal算法的一个实现。除表reps和mgt外,这个算法还使用了另一个表edges,在其中存储图graph所有的边,并调用Python表的sort操作将这些边按权值从小到大排好序。随后的操作就是逐个选择最短的有效边。
边表的形式是(w,vi,vj),其中w是这条边的权值,vi和vj是其两个端点。在主循环里顺序检查edges里的边(按权值从小到大),如果一条边的两端点代表元不同,就将其加人mst,并更新一个连通分量的代表元。这样反复做到mst里积累了n-1条边(成功得到最小生成树),或者所有边都已检查完毕(没有最小生成树),循环结束。这时在mst里保存的是graph的最小生成树,或最小生成树林。
#Kruskal算法
def Kruskal(graph):
vnum=graph.vertex_num()
reps=[i for i in range(vnum)]
mst,edges=[],[]
for vi in range(vnum):
for v,w in graph.out_edges(vi):
edges.append((w,vi,v))
edges.sort()
for w,vi,vj in edges:
if reps[vi]!=reps[vj]:
mst.append(((vi,vj),w))
if len(mst)==vnum-1:
break
rep,orep=reps[vi],reps[vj]
for i in range(vnum):
if reps[i]==orep:
reps[i]=rep
return mst
Prim算法
prim算法是MST性质的直接应用,其基本思想是:从一个顶点出发,利用MST性质选择最短连接边,扩充已连接的顶点集并加入所选的边,直至结点集合里包含了图中的所有顶点。
算法细节
- 从图G的顶点集V中任取一顶点(例如顶点v0)放人集合U中,这时U={v0},令边集合ET={},显然T=(U,ET)是一棵树(只包含一个顶点且没有边)。
- 检查所有一个端点在集合U里而另一个端点在集合V-U的边,找出其中权最小的边e=(u,v)(假设),将顶点v加人顶点集合U,并将e加人边集合。易见,扩充之后的T=(U,ET)仍是一棵树。
- 重复上面步骤直到U=V(所构造的树已经包含了所有顶点)。这时集合ET里有n-1条边,子图T=(U,ET),就是G的一棵最小生成树。
下面算法里用了一个优先队列cands记录候选边,mst的作用与前面算法相同。开始将边(0,0,0)压人队列,表示从顶点0到自身的长度为0的边,第一个元素是权值。然后执行算法的主循环,直到mst记录了”个顶点(成功构造出最小生成树)或优先队列空(说明图graph不连通,没有最小生成树)时结束。
#prim算法
def Prim(graph):
vnum=graph.vertex_num()
mst=[None]*vnum
cands=PrioQue([(0,0,0)])
count=0
while count<vnum and not cands.is_empty():
w,u,v=cands.dequeue()
if mst[v]:
continue
mst[v]=((u,v),w)
count+=1
for vi,w in graph.out_edges(v):
if not mst[vi]:
cands.enqueue((w,v,vi))
return mst