最小生成树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 对象方法是如何实现的 这显然是一个对象方法调用.但对象方法是如何实现的呢?在静态语言中,因为有编译 ...
随机推荐
- 每日一题--Python打印金字塔
def day1(num): s = 'abcdefghijklmnopqrstuvwxyz' * (num // 26 + 1) for i in range(1, num + 1): print( ...
- 【直播预告】HarmonyOS 极客松赋能直播第六期:产品创新从哪里来?
- 重新点亮linux 命令树————压缩和解压缩[四]
前言 简单整理一下压缩和解压缩. 正文 在windows 中我们使用压缩和解压缩一般是7z这个压缩和解压软件,但是在linux中压缩和解压是两个不同的软件. 在最早的linux 备份介质是磁带,使用的 ...
- iOS的cer、p12格式证书解析监控
之前博客写过直接解析ipa包获取mobileprovision文件来监控APP是否过期来,但APP的推送证书还没有做, 大家都知道,iOS的推送证书不会放到ipa包里,只能通过直接解析p12或cer. ...
- 深度解读 MongoDB 最全面的增强版本 4.4 新特性
MongoDB 在今年正式发布了新的 4.4 大版本,这次的发布包含众多的增强 Feature,可以称之为是一个维护性的版本,而且是一个用户期待已久的维护性版本,MongoDB 官方也把这次发布称为「 ...
- Redis消息队列发展历程
简介:Redis是目前最受欢迎的kv类数据库,当然它的功能越来越多,早已不限定在kv场景,消息队列就是Redis中一个重要的功能.Redis从2010年发布1.0版本就具备一个消息队列的雏形,随着1 ...
- 更灵活的边缘云原生运维:OpenYurt 单元化部署新增 Patch 特性
简介: 在正文开始之前,我们先回顾一下单元化部署的概念和设计理念.在边缘计算场景下,计算节点具有很明显的地域分布属性,相同的应用可能需要部署在不同地域下的计算节点上. 作者 | 张杰(冰羽)来源 | ...
- 数据库误操作后悔药来了:AnalyticDB PostgreSQL教你实现分布式一致性备份恢复
简介: 本文将介绍AnalyticDB PostgreSQL版备份恢复的原理与使用方法. 一.背景 AnalyticDB PostgreSQL版(简称ADB PG)是阿里云数据库团队基于Postgr ...
- [FAQ] swagger-php 支持 Authorization Bearer token 校验的用法
@OA\SecurityScheme 可以是 Controller 层面也可以是 Action 层面. 类型 type="apiKey". in="header" ...
- [FE] uni-app Grid 宫格组件 uni-grid 用法
文档上的描述是比较简陋的,不明所以. 核心就是两块内容,一个是 uni-grid 可以加 change 事件:另一个是 uni-grid-item 上面 index 属性值会作为 change 指定函 ...