最小生成树Kruskal算法的实现原理
到底什么是最小生成树
最小生成树算法应用范围比较广,例如在网络的铺设当中难免会出现环路,需要要生成树算法来取出网络中的环,防止网络风暴的发生。那到底什么是最小生成树呢?我这里就不给严谨的定义了,这种定义网上一搜一大堆,但是往往严谨的定义都不太容易理解。下面我就给出一个更容易理解的定义。
在理解“最小生成树”之前,我们需要理解什么是“生成树”。
生成树的概念:在一个连通图中取出这个图的全部顶点和一部分边形成的一个无环图就是这个连通图的生成树。例如在下图中,任意取出图中的一条边都使得这个图中没有环路,也就形成了一棵树。

有人可能会说了,右边生成的图也不像是一棵树呀,其实没有环的图就是一颗树,只不过树中每个节点的度都是不定的。
理解完生成树的概念后,我想最小生成树的概念就很容易理解了:在一个连通图的所有生成树中,边的权重之和最小的那棵树称为最小生成树。话不多说,给朕来张图:

在所有生成树中只有这棵树的权重最小,为6。
因为这个图太简单了,我们一眼就可以看出来去除A<——>B的的这条边后就是最小生成树,那如果图变得非常复杂,就比如这样:

此时我们就需要去寻求最小生成树算法的帮助了。其实算法也就是解决特定问题的一种套路或者说规律,如果自己找不出一些问题解决的思路,就可以去学习前人总结出来的规律了。
Kruskal算法的实现思路
最小生成树算法有多种,例如:Kruskal,Prim。我们这里来对Prim算法不做解释。
第一步:按照边的权重对边进行排序

第二步:从上至下依次取出边,每次取的边如果在树中形成了环路则必须丢弃。直到最后一条顶点被连接在树中则最小生成树生成。

在取出(D,E)这条边时由于与(C,D)、(C,E)产生了回路,所以丢弃这条边。
这里我们就来到了问题的重点了,我们到底如何判断新加进来的边到底会不会形成一个环路呢?这里我们可以使用并查集的。
判断回路的产生
初始状态我们将图中每一个结点都看成一颗树,就比如这样:

此时我们取出排序边集中的第一条边(C,D),我们发现C、D两个节点来自不同的树,这就说明这条边的加入不会形成环路。此时我么需要将D树置成C的子树。就比如这样:

此时我们取出第二条边(C,A),我们发现C、A两个节点属于不同的树,所以(C,A)边的加入不会成环,我们将这条边加到最小生成树的边集当中,此时我们将需要将A树置为C的子树,就比如这样:

我们取出第三条边(C,E),发现C、E仍然属于两个不同的树,所以我们依旧将这条边加入最小生成树的边集当中。然后将E树置为C树的子树,就比如这样:

重点来了,我们取出第四条边(D,E),发现这两个节点都来自同一个树,说明如果我们将(D,E)边加入到生成树的边集中就会形成环路,所以(D,E)这条边就需要舍弃。
我们取出第五条边:(A,B),发现A、B属于不同的节点,这就代表这个边的加入不会成环,我们将这条边加入最小生成树的边集当中。这是我们发现边集中边的数量加一刚好等于节点数,这也就说明,每个节点都已将包含在最小生成树的边集当中了,也就不需要继续向下取排序边集了。

此时我们将边集中的边重构成一张图,也就是一个最小生成树了:

上面就是整个Kruskal的思路
Kruskal算法的实现
/**
* 图的表示形式有多种,例如:邻接表、邻接矩阵、边集等。我们这里使用边集来表示图
*/
public class Hello {
public static class Edge {
String start;
String end;
int distance;
@Override
public String toString() {
return start + "——>" + end;
}
public Edge(String start, String end, int distance) {
this.start = start;
this.end = end;
this.distance = distance;
if (graphNode.get(start) == null) {
graphNode.put(start, new Node(start));
nodeNum++;//图的节点数加一
}
if (graphNode.get(end) == null) {
graphNode.put(end, new Node(end));
nodeNum++;
}
}
}
public static class Node {
String content;
Node parent;
public Node(String content) {
this.content = content;
}
}
public static List<Edge> edges;
public static Set<Edge> tree = new HashSet<>();//用边集表示最小生成树
public static int nodeNum = 0;//图的节数点
public static Map<String, Node> graphNode = new HashMap<>();
public static void main(String[] args) {
//生成一个带权图(用边集表示图)
buildEdges();
//对图的边集进行排序
edges.sort(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.distance - o2.distance;
}
});
//产生最小生成树
for (Edge edge : edges) {
if (tree.size() + 1 == nodeNum) {//如果 边树+1等于图的节点数,就证明最小生成树已经生成了,也就不用遍历后面的带权边了
break;
}
if (isOk(edge)) {
tree.add(edge);
}
}
//打印最小生成树的边
for (Edge edge : tree) {
System.out.println(edge.toString());
}
}
/**
* 为边集里面添加元素,也就是构建一个图
*/
public static void buildEdges() {
edges = new ArrayList<>();
edges.add(new Edge("C", "D", 1));
edges.add(new Edge("C", "A", 1));
edges.add(new Edge("C", "E", 2));
edges.add(new Edge("A", "B", 3));
edges.add(new Edge("D", "E", 3));
edges.add(new Edge("B", "C", 5));
edges.add(new Edge("B", "E", 6));
edges.add(new Edge("B", "D", 7));
edges.add(new Edge("A", "D", 2));
edges.add(new Edge("A", "E", 9));
}
/**
* 获取并查集中指定元素的根节点
*
* @param node
* @return
*/
public static Node getRootNode(Node node) {
Node root = node;
while (root.parent != null) {
root = root.parent;
}
if (node != root) {
node.parent = root;
}
return root;
}
/**
* 判断这条边集是否能够加到最小生成树的边集中。
*
* @param edge
* @return true表示可以成为最小生成树的一条边
*/
public static boolean isOk(Edge edge) {
Node node1 = graphNode.get(edge.start);
Node node2 = graphNode.get(edge.end);
Node root1 = getRootNode(node1);
Node root2 = getRootNode(node2);
if (root1 != root2) {//如果边的两个点当前不属于一个集,那么这条边作为树的一条边就不会形成环路。
root2.parent=root1;//node2所在的树并成node1子树(两棵树合并)
return true;
}
return false;
}
}
运行结果:

最小生成树Kruskal算法的实现原理的更多相关文章
- 万字长文,以代码的思想去详细讲解yolov3算法的实现原理和训练过程,Visdrone数据集实战训练
以代码的思想去详细讲解yolov3算法的实现原理和训练过程,并教使用visdrone2019数据集和自己制作数据集两种方式去训练自己的pytorch搭建的yolov3模型,吐血整理万字长文,纯属干货 ...
- 求最小生成树——Kruskal算法和Prim算法
给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这两个算法其实都是贪心思想的使用,但又能求出最优解.(代码借鉴http://blog.csdn.net/ ...
- 贪心算法-最小生成树Kruskal算法和Prim算法
Kruskal算法: 不断地选择未被选中的边中权重最轻且不会形成环的一条. 简单的理解: 不停地循环,每一次都寻找两个顶点,这两个顶点不在同一个真子集里,且边上的权值最小. 把找到的这两个顶点联合起来 ...
- Kruskal算法的实现
#include "stdio.h" #include "stdlib.h" struct edge { int m; int n; int d; }a[]; ...
- 冷饭新炒:理解Snowflake算法的实现原理
前提 Snowflake(雪花)是Twitter开源的高性能ID生成算法(服务). 上图是Snowflake的Github仓库,master分支中的REAEMDE文件中提示:初始版本于2010年发布, ...
- 转载:最小生成树-Prim算法和Kruskal算法
本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算 ...
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...
- 最小生成树Prim算法和Kruskal算法
Prim算法(使用visited数组实现) Prim算法求最小生成树的时候和边数无关,和顶点树有关,所以适合求解稠密网的最小生成树. Prim算法的步骤包括: 1. 将一个图分为两部分,一部分归为点集 ...
- 最小生成树之Kruskal算法和Prim算法
依据图的深度优先遍历和广度优先遍历,能够用最少的边连接全部的顶点,并且不会形成回路. 这样的连接全部顶点并且路径唯一的树型结构称为生成树或扩展树.实际中.希望产生的生成树的全部边的权值和最小,称之为最 ...
- Atitit paip.对象方法的实现原理与本质.txt
Atitit paip.对象方法的实现原理与本质.txt 对象方法是如何实现的1 数组,对象,字典1 对象方法是如何实现的 这显然是一个对象方法调用.但对象方法是如何实现的呢?在静态语言中,因为有编译 ...
随机推荐
- 成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用
遥感技术主要通过卫星和飞机从远处观察和测量我们的环境,是理解和监测地球物理.化学和生物系统的基石.ChatGPT是由OpenAI开发的最先进的语言模型,在理解和生成人类语言方面表现出了非凡的能力.重点 ...
- 使用 K8S 部署 RSS 全套自托管解决方案- RssHub + Tiny Tiny Rss
前言 什么是 RSS? RSS 是一种描述和同步网站内容的格式,是使用最广泛的 XML 应用.RSS 搭建了信息迅速传播的一个技术平台,使得每个人都成为潜在的信息提供者.发布一个 RSS 文件后,这个 ...
- 【课程汇总】Hello HarmonyOS系列课程,手把手带你零基础入门
HarmonyOS是面向未来.面向全场景的新一代智能终端操作系统,为不同设备的智能化.互联与协同提供了统一的语言,给人们带来简洁.流畅.连续.安全可靠的全场景交互体验. 初识HarmonyOS的开发者 ...
- Groovy反序列化链分析
前言 Groovy 是一种基于 JVM 的开发语言,具有类似于 Python,Ruby,Perl 和 Smalltalk 的功能.Groovy 既可以用作 Java 平台的编程语言,也可以用作脚本语言 ...
- 重新点亮shell————函数[七]
前言 简单整理一下函数. 正文 自定义函数: function fname(){ 命令 } 函数的执行: fname 函数作用范围的变量: local 变量名 函数的参数 $1 $2 $3 .... ...
- mysql 必知必会整理—游标[十四]
前言 简单介绍一下游标. 正文 需要MySQL 5 MySQL 5添加了对游标的支持,因此,本章内容适用于MySQL 5及以后的版本. 有时,需要在检索出来的行中前进或后退一行或多行.这就是使用游标的 ...
- redis 简单整理——HyperLogLog[十三]
前言 简单介绍一下HyperLogLog. 正文 HyperLogLog并不是一种新的数据结构(实际类型为字符串类型),而 是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数 ...
- 重新整理数据结构与算法(c#)——KMP破解[二十七]
前言 很多人把KMP和暴力破解分开,其实KMP就是暴力破解,整个高大上的名字,难道还不是去试错匹配吗? KMP是这样子的,比如说: 绿色部分是我要匹配的. 按照一般写法是这样子的: ABABA 去匹配 ...
- Mui 消息推送
一.push通过H5+实现 简单实现方式:通过轮询服务器是否有新消息推送过来 mui.plusReady(function() {plus.navigator.closeSplashscreen(); ...
- 实战指南:使用 xUnit 和 ASP.NET Core 进行集成测试【完整教程】
引言 集成测试可在包含应用支持基础结构(如数据库.文件系统和网络)的级别上确保应用组件功能正常. ASP.NET Core 通过将单元测试框架与测试 Web 主机和内存中测试服务器结合使用来支持集成测 ...