【数学与算法】Kruskal算法 - 寻找无向图中最小生成树

链接
Kruskal算法 ,也是一种寻找无向图中最小生成树的算法。

1.基本思想:

维持一个森林,森林是很多树的集合。

  • 初始的时候,一共有n棵树,每个节点是一棵树。初始的时候,森林里没有边。
  • 每一轮循环会检查一条边。如果这条边满足某些性质,那么就选中这条边。让两棵树合并起来。每合并一次会减少一棵树。
  • 当只剩下一棵树的时候,终止循环。
  • 每一次循环研究一条边。所以最终循环次数最多不会超过图中边的数量。

2.实例:

算法的输入是图,如下,7个节点和12条边:

在这里插入图片描述

创建一个队列queue,里面存储所有的边edge,所有的边都按照权重weight做排序,权重小的在上面,权重大的在下面:

在这里插入图片描述
用集合T表示被选中的边。

  • 初始的时候:
    集合T是空集,没有边。当前一共有7棵树,每个节点都是一棵树,这些树里面一条边也没有。
    每一次循环研究一条边。

  • 第1轮循环:

    • 取队列顶端的元素,边 e 1 , 4 \color{red}e_{1,4} e1,4权重为1,在图中找到这条边;
    • 判断这条边 e 1 , 4 \color{red}e_{1,4} e1,4的两个节点v1v4是否在同一棵树里面:
      节点v1v4之间没有红色路径,所以节点v1v4不在同一棵树里面,就接受节点v1v4之间的这条边 e 1 , 4 \color{red}e_{1,4} e1,4,在图中用红色标出这条边 e 1 , 4 \color{red}e_{1,4} e1,4,并且把这条边 e 1 , 4 \color{red}e_{1,4} e1,4放进集合T

在这里插入图片描述

  • 第2轮循环:
    • 取队列顶端的元素,边 e 6 , 7 \color{red}e_{6,7} e6,7权重为1,在图中找到这条边;
    • 判断这条边 e 6 , 7 \color{red}e_{6,7} e6,7的两个节点v6v7是否在同一棵树里面:
      节点v6v7之间没有红色路径,所以节点v6v7不在同一棵树里面,就接受节点v6v7之间的这条边 e 6 , 7 \color{red}e_{6,7} e6,7,在图中用红色标出这条边 e 6 , 7 \color{red}e_{6,7} e6,7,并且把这条边 e 6 , 7 \color{red}e_{6,7} e6,7放进集合T
      在这里插入图片描述
  • 第3轮循环:
    • 取队列顶端的元素,边 e 1 , 2 \color{red}e_{1,2} e1,2权重为2,在图中找到这条边;
    • 判断这条边 e 1 , 2 \color{red}e_{1,2} e1,2的两个节点v1v2是否在同一棵树里面:
      节点v1v2之间没有红色路径,所以节点v1v2不在同一棵树里面,就接受节点v1v2之间的这条边 e 1 , 2 \color{red}e_{1,2} e1,2,在图中用红色标出这条边 e 1 , 2 \color{red}e_{1,2} e1,2,并且把这条边 e 1 , 2 \color{red}e_{1,2} e1,2放进集合T

在这里插入图片描述

  • 第4轮循环:
    • 取队列顶端的元素,边 e 3 , 4 \color{red}e_{3,4} e3,4权重为2,在图中找到这条边;
    • 判断这条边 e 3 , 4 \color{red}e_{3,4} e3,4的两个节点v3v4是否在同一棵树里面:
      节点v3v4之间没有红色路径,所以节点v3v4不在同一棵树里面,就接受节点v3v4之间的这条边 e 3 , 4 \color{red}e_{3,4} e3,4,在图中用红色标出这条边 e 3 , 4 \color{red}e_{3,4} e3,4,并且把这条边 e 3 , 4 \color{red}e_{3,4} e3,4放进集合T
      在这里插入图片描述
  • 第5轮循环:
    • 取队列顶端的元素,边 e 2 , 4 \color{red}e_{2,4} e2,4权重为3,在图中找到这条边;
    • 判断这条边 e 2 , 4 \color{red}e_{2,4} e2,4的两个节点v2v4是否在同一棵树里面:
      节点v2v4之间有红色路径可以到达,所以节点v2v4在同一棵树里面,拒绝接受节点v2v4之间的这条边 e 2 , 4 \color{red}e_{2,4} e2,4,否则会创造出一条回路;
      所以本次循环并没有增加边和节点,如下:
      在这里插入图片描述
  • 同理,第6次循环,第7次循环,第8次循环,第9次循环,最终得到,如下:
    在这里插入图片描述
    上面的图,有7个节点,6条边,所有节点都是连通的,任意两条节点之间都有路径,因此终止程序,返回集合T

3.Kruskal算法总结如下:

在这里插入图片描述


4.How to maintain the forest?

我们需要一种数据结构来维持很多棵树,每一轮循环都需要判断两个节点是否属于相同的树,有时候还需要合并两棵树。

4.1 判断两个节点是否属于相同的树

有很多算法都可以判断两个节点之间是否存在一条红色的路径,但是寻找路径的算法都很慢。
最好的办法是使用并查集 - Disjoint Sets

下面是一个无向图,可以看出,该图有三棵树:

把同一棵树的节点都放在同一个集合里面,当前有三棵树,就用三个集合来存三棵树的节点,如下:
在这里插入图片描述
使用并查集的话,可以快速的判断两个节点是否在同一个集合里面。做一次判断的时间复杂度非常低,接近常数时间,时间复杂度为O(1).

4.2 如何合并两棵树?

举个例子:
如果算法接受了节点v3v4之间的这条边,那么我们就需要把这条边连接的两棵树合并起来,只需要对紫色和绿色部这两个集合做Union操作。
在这里插入图片描述
并查集的好处在于可以快速合并集合。Union操作之后,两棵树就合并成了一棵树。
Union操作的时间复杂度接近常数时间,时间复杂度为O(1).


5. Kruskal算法 时间复杂度

该算法的时间复杂度为 O ( m ∗ l o g m ) \color{red}O(m*log{ m}) O(mlogm),其中,m是边的数量。

  • 算法一开始需要对图中的边做排序,排序需要时间 O ( m ∗ l o g m ) \color{red}O(m*log{ m}) O(mlogm)

  • 然后开始做循环,每一轮循环都会从队列里取出一条边,所以循环的次数不会超过边的总数m;

  • 每一轮循环的时间复杂度都接近常数, O ( 1 ) \color{red}O(1) O(1),实际上是比常数稍微高一点,但是小于对数。
    每一轮循环的代价主要有两个来源:

    • 1.一个代价是判断两个节点是不是属于同一棵树;
    • 2.另一个代价是合并两棵树。

    这两种操作的时间复杂度都接近常数。

综上所述:算法的时间复杂度是: O ( m ∗ l o g m ) + m ∗ O ( 1 ) \color{red}O(m*log{ m})+m*O(1) O(mlogm)+mO(1),也就是 O ( m ∗ l o g m ) \color{red}O(m*log{ m}) O(mlogm)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值