最小生成树算法(python实现)

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)(假设u\epsilon V,v\epsilon V-U),将顶点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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值