你好,我是小黄,一名独角兽企业的Java开发工程师。
感谢茫茫人海中我们能够相遇,
俗话说:当你的才华和能力,不足以支撑你的梦想的时候,请静下心来学习,
希望优秀的你可以和我一起学习,一起努力,实现属于自己的梦想。
一、引言
鸽了一周的我又回来了……..
小黄去过生日了~
带你们看下生日蛋糕
大家有没有在生活中遇到这种事情
你们县城需要在几个小区之间进行修路,由于政府资金紧张,不可能所有的小区之间都进行修路,而是利用最少的资金修一条可以连接所有小区的路
如同下图所示:
当然,上述只是一个抽象化的例子,而我们实际生活中,每个小区间的距离也是不一样的,我们怎么使用最小的资金去连接所有的小区呢?
这就牵扯到我们今天的老大哥们:Kruskal 算法和 Prim 算法
这两种算法分别从边和点产生最小生成树,保证了资金的最小性
本篇文章,我们一起走近 Kruskal 算法,探究一下该算法是怎么通过边来确定最小生成树的
当然,有人可能有疑惑,为什么不一次性把 Prim 算法也讲了,结尾我会告诉答案的
二、Kruskal 算法是什么
克鲁斯卡尔(Kruskal)算法是求连通网的最小生成树的一种方法。与普里姆(Prim)算法不同,它的时间复杂度为 O(eloge)(e为网中的边数),所以,适合于求边稀疏的最小生成树 。
因为我们的 Kruskal 算法是以边为单位,所以求一些边稀疏的最小生成树,时间复杂度比较小
我们以下面的小区为例,通过 Kruskal 算法会给我们一条连接所有小区的最短路径
三、Kruskal 算法本质
对于 Kruskal 算法来说,整体使用了 贪心 + 并查集 的思路
有不熟悉并查集的童鞋可以看一下这篇:三分钟带你学会并查集【含状态压缩】
简单来说,我们需要将所有的边放入一个堆中,按照边的大小进行排序,如下所示:1、2、3、6、7、10、12
- 我们把第一个边
1
取出,将C小区
和D小区
合并,目前集合:{C、D}
- 我们把第二个边
2
取出,将A小区
和E小区
合并,目前集合:{C、D}
,{A、E}
- 我们把第三个边
3
取出,将A小区
和B小区
合并,目前集合:{C、D}
,{A、B、E}
- 将第四个边
6
取出,将A小区
和D小区
合并,目前集合:{A、B、E、C、D}
- 将第五个边
7
取出,将B小区
和E小区
合并,由于{A、B、E、C、D}
在一个集合,不进行合并,跳过该边 - 将第六个边
10
取出,将B小区
和C小区
合并,由于{A、B、E、C、D}
在一个集合,不进行合并,跳过该边 - 依次类推…….
最终我们会得到一个路径,这也就是我们的最小生成树
由图得知,我们最小的资金需要:12
四、Kruskal 算法实现
对于 Kruskal 算法,我们需要实现两部分
- 并查集
- 贪心
1、并查集
这里简单的放下并查集的两个关键步骤,其余源码可以关注 爱敲代码的小黄 公众号,回复:算法源码 即可获得算法源码
合并:
1 | java复制代码 // 合并 |
查询:
1 | java复制代码 public boolean isSame(Node node1, Node node2) { |
2、Kruskal 算法
并查集的初始化:
1 | java复制代码 // 赋予初始值 |
比较器(按照边的权重排序):
1 | java复制代码 public static class EdgeComparator implements Comparator<Edge> { |
Kruskal 算法:
1 | java复制代码 public static Set<Edge> kruskalMST(Graph graph) { |
以上图的描述均使用图的形象化描述:图的形象化描述
五、总结
通过以上的描述,我们可以解决我们开头说的那个问题:你们县城需要在几个小区之间进行修路,由于政府资金紧张,不可能所有的小区之间都进行修路,而是利用最少的资金修一条可以连接所有小区的路
同时,对于 Kruskal
的代码也需要多写几遍,博主写这篇博客的时候,又忘记了怎么写(逃
对源代码有兴趣的小伙伴,可以关注 爱敲代码的小黄 公众号,回复:算法源码 即可获得算法源码
回答一下一开始的问题:有人可能有疑惑,为什么不一次性把 **Prim 算法也讲了**
答:下期讲 Prim,可以水一期(逃
本期的内容就到这里,下期会讲述最小生成树 Prim 算法
我是一名独角兽企业的Java开发工程师,希望可以点个关注呀,有问题可以留言或者私信加我微信:hls1793929520,我们下期再见!
本文转载自: 掘金