到底什么是最小生成树

最小生成树算法应用范围比较广,例如在网络的铺设当中难免会出现环路,需要要生成树算法来取出网络中的环,防止网络风暴的发生。那到底什么是最小生成树呢?我这里就不给严谨的定义了,这种定义网上一搜一大堆,但是往往严谨的定义都不太容易理解。下面我就给出一个更容易理解的定义。

在理解“最小生成树”之前,我们需要理解什么是“生成树”。

生成树的概念在一个连通图中取出这个图的全部顶点和一部分边形成的一个无环图就是这个连通图的生成树。例如在下图中,任意取出图中的一条边都使得这个图中没有环路,也就形成了一棵树。

有人可能会说了,右边生成的图也不像是一棵树呀,其实没有环的图就是一颗树,只不过树中每个节点的度都是不定的。

理解完生成树的概念后,我想最小生成树的概念就很容易理解了:在一个连通图的所有生成树中,边的权重之和最小的那棵树称为最小生成树。话不多说,给朕来张图:

在所有生成树中只有这棵树的权重最小,为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算法的实现原理的更多相关文章

  1. 万字长文,以代码的思想去详细讲解yolov3算法的实现原理和训练过程,Visdrone数据集实战训练

    以代码的思想去详细讲解yolov3算法的实现原理和训练过程,并教使用visdrone2019数据集和自己制作数据集两种方式去训练自己的pytorch搭建的yolov3模型,吐血整理万字长文,纯属干货 ...

  2. 求最小生成树——Kruskal算法和Prim算法

    给定一个带权值的无向图,要求权值之和最小的生成树,常用的算法有Kruskal算法和Prim算法.这两个算法其实都是贪心思想的使用,但又能求出最优解.(代码借鉴http://blog.csdn.net/ ...

  3. 贪心算法-最小生成树Kruskal算法和Prim算法

    Kruskal算法: 不断地选择未被选中的边中权重最轻且不会形成环的一条. 简单的理解: 不停地循环,每一次都寻找两个顶点,这两个顶点不在同一个真子集里,且边上的权值最小. 把找到的这两个顶点联合起来 ...

  4. Kruskal算法的实现

    #include "stdio.h" #include "stdlib.h" struct edge { int m; int n; int d; }a[]; ...

  5. 冷饭新炒:理解Snowflake算法的实现原理

    前提 Snowflake(雪花)是Twitter开源的高性能ID生成算法(服务). 上图是Snowflake的Github仓库,master分支中的REAEMDE文件中提示:初始版本于2010年发布, ...

  6. 转载:最小生成树-Prim算法和Kruskal算法

    本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算 ...

  7. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

  8. 最小生成树Prim算法和Kruskal算法

    Prim算法(使用visited数组实现) Prim算法求最小生成树的时候和边数无关,和顶点树有关,所以适合求解稠密网的最小生成树. Prim算法的步骤包括: 1. 将一个图分为两部分,一部分归为点集 ...

  9. 最小生成树之Kruskal算法和Prim算法

    依据图的深度优先遍历和广度优先遍历,能够用最少的边连接全部的顶点,并且不会形成回路. 这样的连接全部顶点并且路径唯一的树型结构称为生成树或扩展树.实际中.希望产生的生成树的全部边的权值和最小,称之为最 ...

  10. Atitit paip.对象方法的实现原理与本质.txt

    Atitit paip.对象方法的实现原理与本质.txt 对象方法是如何实现的1 数组,对象,字典1 对象方法是如何实现的 这显然是一个对象方法调用.但对象方法是如何实现的呢?在静态语言中,因为有编译 ...

随机推荐

  1. Blocks(单调栈)

    题干中说每次选择一个大于k的数,还要选他左右两个数其中之一加上一,最后问你最长的每个数不小于K的子序列. 这些都是障眼法,其实就是问你最长的平均值大于或等于K的最长子序列,这样就明朗了. 接下来就是找 ...

  2. How to install Django-Install Python Django | Django 安装指南【官方版】

    How to install Django¶ This document will get you up and running with Django. Install Python--Linux ...

  3. redis 简单整理——缓存设计[三十二]

    前言 简单整理一下缓存设计. 正文 缓存的好处: ·加速读写:因为缓存通常都是全内存的(例如Redis.Memcache),而 存储层通常读写性能不够强悍(例如MySQL),通过缓存的使用可以有效 地 ...

  4. WPF开发随笔收录-操作注册表

    一.前言 在windows平台软件开发过程中,注册表的操作是经常会遇到的一个场景.今天记录一下在操作注册表时遇到的一些坑: 二.正文 1.操作注册表,于是直接从网上找了一段代码来用 /// <s ...

  5. 力扣233(java)-数字1的个数(困难)

    题目: 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数. 示例 1: 输入:n = 13输出:6示例 2: 输入:n = 0输出:0 提示: 0 <= n <= ...

  6. 当 Knative 遇见 WebAssembly

    简介: Knative 可以支持各种容器化的运行时环境,我们今天来探索一下利用 WebAssembly 技术作为一个新的 Serverless 运行时. 作者:易立 Knative 是在 Kubern ...

  7. es实战-使用IK分词器进行词频统计

    ​简介:通过IK分词器分词并生成词云. 本文主要介绍如何通过 IK 分词器进行词频统计.使用分词器对文章的词频进行统计,主要目的是实现如下图所示的词云功能,可以找到文章内的重点词汇.后续也可以对词进行 ...

  8. Spring Boot Serverless 实战 | Serverless 应用的监控与调试

    ​简介:Spring Boot 是基于 Java Spring 框架的套件,它预装了 Spring 的一系列组件,让开发者只需要很少的配置就可以创建独立运行的应用程序.在云原生的环境中,有大量的平台可 ...

  9. 阿里云 EDAS 3.0 助力唱鸭提升微服务幸福感

    简介: EDAS 3.0 提供的微服务治理,很好的支持了唱鸭 APP 实现微服务应用的发布.监控.管理等日常业务场景.作为运维侧的重要平台和开框架的提供者,EDAS 3.0 帮助用户可以更专注业务.微 ...

  10. 开源 Serverless 里程碑:Knative 1.0 来了

    ​简介:近期Knative发布了1.0版本,达到了一个重要的里程碑.Knative自2018年7月首次发布以来, 版本不断的迭代发展,除了无数的错误修复.稳定性和性能增强之外,按时间顺序还进行了一些改 ...