KruskalAlgorithm(克鲁斯卡尔算法)

article/2025/11/10 19:28:18

KruskalAlgorithm介绍

  1. 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法。
  2. 基本思想:按照权值从小到大的顺序选择 n-1 条边,并保证这 n-1 条边不构成回路
  3. 具体做法:首先构造一个只含 n 个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止

最小生成树

(Minimum Cost Spanning Tree),简称 MST。给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树

克鲁斯卡尔算法应用场景-公交站问题

image-20210128225955226

  1. 某城市新增 7 个站点(A, B, C, D, E, F, G) ,现在需要修路把 7 个站点连通

  2. 各个站点的距离用边线表示(权) ,比如 A – B 距离 12 公里

  3. 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

克鲁斯卡尔算法图解说明

以上图为例,来对克鲁斯卡尔进行演示(假设用数组 R 保存最小生成树结果)。

image-20210128230230554 image-20210128230322033

image-20210128230504475image-20210128230558289

image-20210128230838042 image-20210128230946838

第1步:将边<E,F>加入 R 中。

边<E,F>的权值最小,因此将它加入到最小生成树结果 R 中。

第2步:将边<C,D>加入 R 中。

上一步操作之后,边<C,D>的权值最小,因此将它加入到最小生成树结果 R 中。

第3步:将边<D,E>加入 R 中。

上一步操作之后,边<D,E>的权值最小,因此将它加入到最小生成树结果 R 中。

第4步:将边<B,F>加入 R 中。

上一步操作之后,边<C,E>的权值最小,但<C,E>会和已有的边构成回路;因此,跳过边<C,E>。同理,跳过边<C,F>。将边<B,F>加入到最小生成树结果 R 中。

第5步:将边<E,G>加入 R 中。

上一步操作之后,边<E,G>的权值最小,因此将它加入到最小生成树结果 R 中。

第6步:将边<A,B>加入 R 中。
上一步操作之后,边<F,G>的权值最小,但<F,G>会和已有的边构成回路;因此,跳过边<F,G>。同理,跳过边<B,C>。将边<A,B>加入到最小生成树结果 R 中。

此时,最小生成树构造完成!它包括的边依次是:***<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>***。

克鲁斯卡尔算法分析

根据前面介绍的克鲁斯卡尔算法的基本思想和做法,我们能够了解到,克鲁斯卡尔算法重点需要解决的以下两个问题:

问题一 对图的所有边按照权值大小进行排序。

问题二 将边添加到最小生成树中时,怎么样判断是否形成了回路。

问题一很好解决,采用排序算法进行排序即可。

问题二,处理方式是:记录顶点在"最小生成树"中的终点,顶点的终点是"在最小生成树中与它连通的最大顶点"。然后每次需要将一条边添加到最小生存树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路。

如何判断是否构成回路

image-20210128231909486

在将<E,F> <C,D> <D,E>加入到最小生成树 R 中之后,这几条边的顶点就都有了终点:

  • C 的终点是 F。

  • D 的终点是 F。

  • E 的终点是 F。

  • F 的终点是 F。

关于终点的说明:

  1. 就是将所有顶点按照从小到大的顺序排列好之后;某个顶点的终点就是"与它连通的最大顶点"。

  2. 因此,接下来,虽然<C,E>是权值最小的边。但是 C 和 E 的终点都是 F,即它们的终点相同,因此,将<C,E>加入最小生成树的话,会形成回路。这就是判断回路的方式。也就是说,我们加入的边的两个顶点不都指向同一个终点,否则将构成回路

代码实现

public class KruskalAlgorithm {private int edgeNum; // 边的个数private char[] vertexs; // 顶点数组private int[][] matrix; // 邻接矩阵// 用 INF 表示两个顶点之间不连通private static final int INF = Integer.MAX_VALUE;public static void main(String[] args) {char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};int[][] matrix = {/*A*//*B*//*C*//*D*//*E*//*F*//*G*//*A*/{  0,   12,  INF, INF, INF,  16,  14},/*B*/{ 12,    0,   10, INF, INF,   7, INF},/*C*/{INF,   10,    0,   3,   5,   6, INF},/*D*/{INF,  INF,    3,   0,   4, INF, INF},/*E*/{INF,  INF,    5,   4,   0,   2,   8},/*F*/{ 16,    7,    6, INF,   2,   0,   9},/*G*/{ 14,  INF,  INF, INF,   8,   9,   0}};KruskalAlgorithm kruskal = new KruskalAlgorithm(vertexs, matrix);kruskal.print();kruskal.kruskal();}// 构造器public KruskalAlgorithm(char[] vertexs, int[][] matrix) {this.vertexs = vertexs;this.matrix = matrix;// 统计边for (int i = 0; i < vertexs.length; i++) {for (int j = i+1; j < vertexs.length; j++){if (this.matrix[i][j] != INF) {this.edgeNum++;}}}}// Kruskal核心算法public void kruskal() {int index = 0; //EData[] results = new EData[edgeNum]; // 用来保存最后的最小生成树int[] ends = new int[edgeNum]; // 用来保存每个顶点在最小生成树中的终点下标// 获取图中的所有边EData[] edges = getEdges();// 将边按照权值大小进行排序,因为Kruskal算法是从权值最小的开始挑sortEdges(edges);System.out.println("图的边集合");System.out.println(Arrays.toString(edges));/*遍历 edges 数组,判断要加入的边是否形成回路,如没有则加入最小生成树 results 中,否则不加入*/for (int i = 0; i < this.edgeNum; i++) {// 用来表示第 i 条边的一个顶点(起点)int p1 = getPosition(edges[i].start);// 用来表示第 i 条边的另一个顶点(终点)int p2 = getPosition(edges[i].end);// 获取p1这个顶点在当前最小生成树中的终点int m = getEnd(ends, p1);// 获取p2这个顶点在当前最小生成树中的终点int n = getEnd(ends, p2);// 判断两个终点是否相同,即是否构成回路if (m != n) { // 没有构成回路ends[m] = n; // 例如 -> <E,F> : [0,0,0,0,5,0,0,...]results[index++] = edges[i]; //加入到最小生成树中}}// 打印"最小生成树"System.out.println("最小生成树为");for (int i = 0; i < index; i++) {System.out.println(results[i]);}}// 打印邻接矩阵public void print() {System.out.println("===邻接矩阵===");for (int[] link : this.matrix){System.out.println(Arrays.toString(link));}}// 对边进行排序处理public void sortEdges(EData[] edges) {EData temp;for (int i = 0; i < edges.length - 1; i++) {for (int j = 0; j < edges.length - 1 - i; j++) {if (edges[j].weight > edges[j+1].weight) {temp = edges[j];edges[j] = edges[j+1];edges[j+1] = temp;}}}}/*** @param ch 表示顶点的值 'A', 'B' ...* @return 返回顶点在顶点数组中对应的下标,如找不到,则返回-1*/public int getPosition(char ch){for (int i = 0; i < vertexs.length; i++) {if (vertexs[i] == ch) {return i;}}return -1;}/*** 从邻近矩阵中找到连通的边* @return 形如{['A', 'B', 12], ['B', 'F', 7], ...}*/private EData[] getEdges() {int index = 0;EData[] edges = new EData[this.edgeNum];for (int i = 0; i < this.vertexs.length; i++) {for (int j = i+1; j < this.vertexs.length; j++) {if (this.matrix[i][j] != INF) {edges[index++] = new EData(this.vertexs[i], this.vertexs[j], this.matrix[i][j]);}}}return edges;}/*** 获取下标为 i 的顶点的终点,用于判断两个顶点的终点是否相同* @param ends 用来记录各个顶点对应的终点下标* @param i 表示传入顶点对应的下标* @return 返回下标为 i 的这个顶点对应的终点下标*/private int getEnd(int[] ends, int i){while (ends[i] != 0) { // 不断的往下找,直到找到这个顶点的终点i = ends[i];}return i;}
}// 创建一个类-EData,用来表示一条边和边上的两个顶点
class EData {char start; // 边的起点char end; // 边的终点int weight; // 边的权值public EData(char start, char end, int weight) {this.start = start;this.end = end;this.weight = weight;}@Overridepublic String toString() {return "<" + start + "," + end + ">" + ":" + weight;}
}

运行结果

image-20210128232456353

:以上大部分内容来源于韩顺平老师的数据结构和算法笔记


http://chatgpt.dhexx.cn/article/iwHSwmix.shtml

相关文章

数据结构——克鲁斯卡尔(Kruskal)算法

克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同&#xff0c;它的时间复杂度为O&#xff08;eloge&#xff09;&#xff08;e为边数&#xff09;&#xff0c;适合于求边稀疏的网的最小生成树 。克鲁斯卡尔算法从另一途径求网的最小生成树。其基本思想是&a…

克鲁斯卡尔(Kruskal)算法(严蔚敏C语言)

克鲁斯卡尔算法(Kruskal) ​ 克鲁斯卡尔算法是求连通网的最小生成树的另一种方法。与普里姆算法不同&#xff0c;它的时间复杂度为O&#xff08;eloge&#xff09;&#xff08;e为网中的边数&#xff09;&#xff0c;所以&#xff0c;适合于求边稀疏的网的最小生成树 。 ——百…

4.《学会提问》

1.书中的观点 批判性思维会教你很多技能和态度&#xff0c;让你理性地找到对自己有意义的答案并为此感到自豪。批判性思维鼓励你倾听他人&#xff0c;向别人学习&#xff0c;同时掂量别人所说的话&#xff0c;看看它们的分量如何。如此&#xff0c;你将了解到我们必须要依赖他人…

如何提问——提问的艺术

转存失败重新上传取消https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md

《提问的艺术》读书笔记

分享读《提问的艺术》的xmind总结&#xff0c;每周听一本书&#xff0c;打卡第一周

(转载) 提问的艺术

虽然这是老话常谈&#xff0c;但是最近的回答问题的过程中&#xff0c;有点感触。你问题问的好&#xff0c;问的准确&#xff0c;回答你的人才有积极性给你答复&#xff0c;这样你又可以更快的解决你的问题。好多人不知道如何提问&#xff0c;所以我打算把这篇老文章转过来置顶…

论程序员提问的艺术

最近工作比较忙&#xff0c;加上空闲时间大部分都是在维护开发【云狗AI】&#xff0c;所以也有一段时间没更新视频了&#xff0c;有不懂的&#xff0c;也可以问一下【云狗AI】以后我也会花更多的时间在维护这个项目中。争取给大家带来更好的体验。 主要是因为最近没发现什么特…

提问的艺术[转]

这是刚看到的 所以转一下 也是警示自己的伸手行为 原文&#xff1a;How To Ask Questions The Smart Way 译文链接&#xff1a;提问的艺术 作者&#xff1a; 艾瑞克.史蒂文.雷蒙德(Eric Steven Raymond) <esrthyrsus.com> 瑞克.莫恩(Rick Moen) <respond-autolinuxmaf…

【提问的艺术】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

提问的艺术/怎么高效的提问

唉,可能是因为见过的人多了吧,不管是在现实世界中,还是在网络上,总是需要向别人询问一些东西的, 有的人来问,谦谦有礼,感觉很好,帮助了别人,也没损失什么,有的人来问,就是真心不想搭理他,搭理了他就是 浪费自己的时间,还心理不好受的. 今天不说现实中怎么提问.就说在互联网上.…

《提问的艺术》读后感

前言提问前 他明明能帮到我却不帮我提问前必知必会的一些事关于搜索引擎 提问时 找准对象学会停顿组织你的问题清晰的发问低声下气代替不了做自己的家庭作业删除无意义的要求不要把问题标记为紧急 即使对你而言的确如此礼貌总是有益的对待无礼提问禁区 总结 前言 众所周知&am…

提问的艺术,原文链接

你提的问题没有任何有用的信息&#xff0c;u hava a poop。 提问的艺术 今天在拉屎的时候看到QQ群里有人问问题&#xff0c;那个问题太他妈蠢了&#xff0c;所以我就写了这一片博客&#xff0c;如果再有人问愚蠢的问题我就把这篇博客丢给他。 提问的艺术 原文链接&#xff…

《提问的艺术:如何快速获得答案》(精读版)

《提问的艺术》《How-To-Ask-Questions-The-Smart-Way》 https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way © 2001 Eric Steve Raymond © 2001 D.H.Grand(nOBODY/Ginux) © 2012 Conmajia《提问的艺术&#xff1a;如何快速获得答案》原著Eric…

Serverlet理解

部分转载自&#xff1a;https://blog.csdn.net/javaloveiphone/article/details/8154791 从上图可以看出 Tomcat 的容器分为四个等级&#xff0c;真正管理Servlet 的容器是Context 容器&#xff0c;一个 Context 对应一个 Web 工程。除了将 Servlet 包装成 StandardWrapper 并作…

java serverlet_Serverlet程序

Serverlet是用Java编写的服务器端程序;主要用于交互地浏览和修改数据&#xff0c;生成动态Web内容; 一个serverlet就是一个继承于HttpServlet抽象类的Java类&#xff1b;下面先看一个简单的例子 import javax.servlet.*;importjavax.servlet.http.HttpServlet;importjavax.serv…

serverlet 区别_jsp serverlet 区别

JSP和Servlet的概念对于JSP初学者来说比较不清楚&#xff0c;以下总结一些个人看法&#xff1a; (1).简单的来说Jsp就是含有Java代码的html&#xff0c;而servlet是含有html的Java代码&#xff1b; (2).Jsp最终也是被解释为servlet并编译再执行&#xff0c;Jsp不过是servlet的另…

Java开发之ServLet详解

一、什么是ServLet&#xff1f; serverLet是javaEE中运行于服务器端的&#xff0c;用于接收和响应HTTP协议的请求的程序。 二、ServLet的三种实现方式 1、实现ServLet接口 步骤&#xff1a; &#xff08;1&#xff09;实现ServLet接口 &#xff08;2&#xff09;重写包括s…

Serverlet的生命周期

1.Servlet的生命周期 Servlet没有main()方法&#xff0c;不能独立运行&#xff0c;它的运行完全由Servlet引擎来控制和调度。所谓生命周期 &#xff0c;指的是servlet容器何时创建servlet实例、何时调用其方法进行请求的处理、何时并销毁其实例的 整个过程。其完整的周期包括…

VRRP协议以及关联Track详解

一、实验 详细解释可以直接查看下面连接&#xff0c;是我转发CSDN大佬的连接&#xff0c;直接上实验 VRRP详解 R1&#xff0c;R2充当两个网段的PC、分别是13.1.1.0 和24.2.2.0 R5&#xff0c;R6充当两个PC的接入层交换机 R5 eth0/1属于vlan 2000&#xff0c;R6 eth0/2属于vlan…

理解VRRP协议

VRRP即虚拟路由冗余协议(Virtual Router Redundancy Protocol)&#xff0c;它是为了避免路由器出现单点故障的一种容错协议。 如图1所示&#xff0c;我们把多个运行着VRRP协议的路由器抽象成一个虚拟路由器&#xff08;从外边来看&#xff0c;就像只有一个真实的路由器在工作&…