最小生成树:克鲁斯卡尔算法实现


title: ‘最小生成树:克鲁斯卡尔算法实现’
date: 2019-09-03 19:37:50
tags: python,数据结构
categories: 计算机理论

克鲁斯卡尔算法

算法原理及流程

原理

在一个连通图中不断选取权值最小的边,然后连起来,就是这样。

假设给定图G,结果图T

基本步骤如下:

  1. 将G中的所有边按权值递增的顺序进行排序
  2. 选择权值最短的边且边的两端点属于不同连通分量(如果两端点属于同一个连通分量中,那么就说明该子图是连通图!所以不行),然后该边与T中已选择的边进行连接,每次边连接都会使得T的连通分量减1
  3. 当边数小于顶点数时,不断重复1,2

如果当做完上面这些步骤后,得出的结果T中不能包含G中的所有顶点,则说明原图G是不连通的(也就是不是任意一个节点到另一个节点都走的通)

这里有两个难点:

  1. 最短边的选取

    思路①:采用优先队列,在python中可以通过优先级队列实现,其他语言像C++,Java中也有类似实现的数据结构。

    思路②:不断的扫描候选边列表,然后进行排序。这种方法就比较麻烦了,写的代码比较多,不过也很灵活,具体排序方式你可以选择。

  2. 如何判断边的两个端点的连通分量

    思路①:不断的检查两个端点之间是否有路径,有路径就说明在同一个子图中,连通分量相同。不过这样也太麻烦了点还浪费计算时间

    思路②:前人提出的一种方法,为每个连通分量确定一个代表元,如果两个顶点的代表元相同,则表示他们连通成环。例如下图

    v1
    V2
    V3
    V4

    当初始化的时候v1,v2,v3,v4的代表元就是他们的序号也就对应0,1,2,3

    v1 v2 构成新边的时候,就要把v2的代表元改为v1 的代表元0

    这时候v1,v2,v3,v4的代表元就更新为0,0,2,3

    v1
    V2
    V3
    V4

    v1 v2是的连通分量相同并且他们的代表元也是相同的。

    类似,如果想要连接v2 v3,此时v2 v3 的代表元不同,因此连接了也不会构成环。 直接把v3的代表元修改为v2的代表元即可,即0

    v1
    V2
    V3
    V4

    此时v1 v2 v3是连通的,他们的代表元是0,0,0

    如果下一次操作中,想要把v3 连接到v1 ,检查他们的代表元,都是0所以连接起来一定会构成环

    v1
    V2
    V3
    V4

    因此,可以使用代表元判断欲加入的边是否会与已选择集合T中的边构成环路。

构成过程举例

假设还是之前的这颗图G,其结果集T中目前还为空

6
1
5
5
5
6
4
V1
V2
V3
V4
V5
V6
初始化

全部边入队,自动在优先队列中根据权值排好序

v4,v6,2
V5,V6,6
v2,v3,5
v3,v4,5
v3,v5,6
v3,v6,4
v2,v5,3
v1,v4,5
v1,v2,6
v1,v3,1

并且初始化代表元列表,初始值为他们的下标。

例如v1的代表元初始值为1,v2的代表元初始值为2…vn的代表元初始值为n

v1,1
v2,2
v3,3
v4,4
v5,5
v6,6
第一次构造
  1. 队首出队,所以<v1,v3>边出队
v6,v4,2
V6,V5,6
v3,v2,5
v3,v4,5
v3,v5,6
v3,v6,4
v2,v5,3
v1,v4,5
v1,v2,6
v1,v3,1
  1. 检查v1 v3的代表元,很明显不同,所以将<v1,v3>加入T集合中
1
v1
V3
  1. 合并代表元,修改等于代表元值为3的代表元的值,改为v1的代表元即1
v1,1
v2,2
v3,1
v4,4
v5,5
v6,6
第二次构造
  1. 队首出队,所以<v4,v6>边出队
v4,v6,2
V5,V6,6
v2,v3,5
v3,v4,5
v3,v5,6
v3,v6,4
v2,v5,3
v1,v4,5
v1,v2,6
  1. 检查v4 v6的代表元,很明显不同,所以将<v4,v6>加入T集合中
1
2
v1
V3
V6
V4
  1. 合并代表元,修改等于代表元值为6的代表元的值,改为v4的代表元的值即4
v1,1
v2,2
v3,1
v4,4
v5,5
v6,4
第三次构造
  1. 队首出队,所以<v2,v5>边出队
V5,V6,6
v3,v2,5
v3,v4,5
v3,v5,6
v3,v6,4
v2,v5,3
v1,v4,5
v1,v2,6
  1. 检查v2 v5的代表元,很明显不同,所以将<v2,v5>加入T集合中
1
2
3
v1
V3
V6
V4
V2
V5
  1. 合并代表元,修改等于代表元值为5的代表元的值,改为v2的代表元的值即2
v1,1
v2,2
v3,1
v4,4
v5,2
v6,4
第三次构造
  1. 队首<v3,v6>出队
v5,V6,6
v3,v2,5
v3,v4,5
v3,v5,6
v3,v6,4
v1,v4,5
v1,v2,6
  1. 检查v3 v6的代表元,不同,所以将<v3,v6>加入T集合中
1
2
3
4
v1
V3
V6
V4
V2
V5
  1. 合并代表元,修改等于代表元值为4的代表元的值,改为v3代表元的值即1
v1,1
v2,2
v3,1
v4,1
v5,2
v6,1
第四次构造
  1. 队首<v1,v4>出队
V5,V6,6
v3,v2,5
v3,v4,5
v3,v5,6
v1,v4,5
v1,v2,6
  1. 检查v1 v4的代表元,相同,所以不将<v1,v4>加入T集合中
1
2
3
4
v1
V3
V6
V4
V2
V5
  1. 此时代表元不进行任何操作
v1,1
v2,2
v3,1
v4,1
v5,2
v6,1
第五次构造
  1. 队首<v3,v4>出队
V5,V6,6
v3,v2,5
v3,v4,5
v3,v5,6
v1,v2,6
  1. 检查v3 v4的代表元,相同,所以不将<v3,v4>加入T集合中
1
2
3
4
v1
V3
V6
V4
V2
V5
  1. 此时代表元不进行任何操作
v1,1
v2,2
v3,1
v4,1
v5,2
v6,1
第六次构造
  1. <v2,v3>出队
V5,V6,6
v3,v2,5
v3,v5,6
v1,v2,6
  1. 检查v2 v3的代表元,不同,所以将<v2,v3>加入T集合中
1
2
3
4
5
v1
V3
V6
V4
V2
V5
  1. 合并代表元,修改等于代表元为2的代表元的值,改为v3代表元的值即1
v1,1
v2,1
v3,1
v4,1
v5,1
v6,1

可以发现,当前T集合中已经有5条边了,最小生成树已经生成完毕。同时观察到,代表元中的值也相同了,表示他们都在同一个子图中了。

算法实现

    # kruskal算法,最小生成树,前提该图必须是连通网
    def kruskal(self):
        # 初始化代表元和结果图
        result, reps, pqueue, edgesCount = GraphAL(graph={}), {}, [], 0
        for key in self._graph.keys():
            reps[key] = key
        # 边入队,按优先级排序,选出最短边
        for key in self._graph:
            for end in self._graph[key].keys():
                edges = self._graph[key][end]
                heapq.heappush(pqueue, (edges, key, end))  # 边入队
            pass
        # 当边数达到n-1条时,即成功得到最小生成树时停止
        while edgesCount < self.get_vertexNum() - 1 and not pqueue.__len__() == 0:
            # 出队
            pair = list(heapq.heappop(pqueue))
            # 判断是否有该顶点,如果没有就要加入
            if pair[1] not in result._graph:
                result.add_vertex(pair[1])
            if pair[2] not in result._graph:
                result.add_vertex(pair[2])
            # 检查两点是否属于不同连通分量
            if reps[pair[1]] != reps[pair[2]]:
                result.add_edge(pair[1], pair[2], pair[0])
                edgesCount += 1
                # 合并连通分量
                rep, orep = reps[pair[1]], reps[pair[2]]
                for key in reps.keys():
                    if reps[key] == orep:
                        reps[key] = rep
        return result
        pass

测试

20190831002634

与刚才结果自动推的完全一致。

1
2
3
4
5
v1
V3
V6
V4
V2
V5
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值