介绍

Kruskal算法与Prim算法不同,Prim是以顶点为向导,通过遍历顶点不断寻找与之相连的最小的权值边,从而找到最小生成树。Kruskal算法是以边为向导,依次找出权值最小的边建立最小生成树,每次新增的边要保证不能使生成树构成回路,直到遍历完所有的边为止。

核心思想

Kruskal的核心算法即在如何验证所添加的边是否与已添加的边构成回路,从而进行边的取舍。如图1所示,根据左图构建数组edges[]用于保存每条边对象,该对象包括begin、end和weight属性,分别表示当前边的起始顶点、终点和权值。对edges进行从小到大的排序后,得到如右图所示的数组。我们从小到大选择权值最小的边,每添加一条边,则图中会连接两个顶点。我们只需保证每次新添加的边不会与之前添加的边构成回路,那么,当所有的边遍历结束后,就形成了最小生成树。

如何验证回路

我们构建一个特殊的长度为边数的parent数组,该数组的角标表示某条边的起点,值为该边的终点,初始化时parent数组全为0,即表示没有一条边加入。我们通过角标,即起始顶点,可以找到该起始顶点所连接的终点,即邻接点;再以该终点作为新的起始顶点,又可以找到下一个终点。当找到的终点为0时,表示该以该终点为起点的顶点不存在邻接点;当以边的起点为起点找到的最终终点,与以边的终点为起点找到的最终终点相等时,说明该边会与之前的边构成回路。下面通过图1中的图来说明。

当遍历到edge[0]时,parent[4]=0,表示尚未加入以v4为顶点的边,故将edge[0]边的起始顶点和终点加入到parent数组中,此时parent=[0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],即parent[4]=7,表示存在以顶点v4为起点,v7为终点的通路,这里即是edges[0]边。依次向下遍历,由于parent数组初始为0,故在edge[6]之前不存在构成回路的边,如下图所示,此时parent=[1, 5, 8, 7, 7, 8, 0, 0, 6, 0, 0, 0, 0, 0, 0]。

当遍历到edge[7]时,edges[7]的起始顶点为5,我们以edges[7]的起始顶点为起点,在parent数组中查找其最终终点,有如下过程:

parent[5]=8 --> parent[8]=6 --> parent[6]=0 故尚不存在以v6为起始顶点的边,最终终点为v6

注意:此处从parent[5]=8并不表示顶点v5和v8是直接相连的,而是表示v5顶点最终会通过某种连接连通到v8,以此类推,我们可以得知v5最终会连通到v6

以edge[7]的终点为起点,在parent数组中查找器最终终点,有如下过程:

parent[6]=0 故尚不存在以v6为起始顶点的边,最终终点即为起点v6

此时,我们发现以edges[7]的起点和终点的搜索结果相等,故可以得到,当加入edges[7]边时,会与图中已经存在的边构成回路,所以我们将其舍弃,如下图所示。

以此类推,当所有的边都遍历完成之后,图中的顶点所构成的回路即为最小生成树,如下图:

代码实现

package Kruskal;/*** 克鲁斯卡尔算法* @author asus*/
public class Kruskal {private Edge[] edges; // 按权值从小到大排序后的边数组private int edgeSize;  // 边数public Kruskal(int edgeSize) {this.edgeSize = edgeSize;edges = new Edge[edgeSize];}//定义边的内部类class Edge {private int begin;private int end;private int weight;public Edge(int begin, int end, int weight) {super();this.begin = begin;this.end = end;this.weight = weight;}public int getBegin() {return begin;}public void setBegin(int begin) {this.begin = begin;}public int getEnd() {return end;}public void setEnd(int end) {this.end = end;}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}}//通过邻接矩阵创建数组public Edge[] createEdgeArray(int[][] matrix) {     int k = 0;for (int i = 0; i < matrix.length; i++) {for (int j = i + 1; j < matrix[i].length; j++) {int m = matrix[i][j];if (matrix[i][j] == Integer.MAX_VALUE)continue;edges[k] = new Edge(i, j, matrix[i][j]);k++;}}for (int i = 0; i < edges.length; i++) {for (int j = i + 1; j < edges.length; j++) {if (edges[j].weight < edges[i].weight) {Edge e = edges[i];edges[i] = edges[j];edges[j] = e;}}}return edges;}public void miniSpanTreeKruskal() {int m, n, sum = 0;int[] parent = new int[edgeSize];// 用于验证是否构成回路,角标为某条边的起点,值为该边的终点for (int i = 0; i < parent.length; i++) {parent[i] = 0;}for (int i = 0; i < parent.length; i++) {m = find(parent, edges[i].begin);// 以edges[i]边的起点为检索起点,找到其可以连接到的最终终点n = find(parent, edges[i].end);// 以edges[i]边的终点为检索起点,找到其可以连接到的最终终点if (m != n) {// 新加入的edges[i]边不会和已加入的边构成回路parent[m] = n;System.out.println("连接起点:" + edges[i].begin + ",连接终点:" + edges[i].end + ",权值为:" + edges[i].weight);sum += edges[i].weight;} else {System.out.println("边" + i + "构成了回路");}}System.out.println("sum:" + sum);}/*** 在图中以node为搜索起点进行检索,最终返回检索到的终点。即表示,从起点node开始,不论以何种路径检索,最终会连接到终点。* * @param parent 用于检验是否构成回路的数组* @param node   起始顶点,在parent中为角标* @return 返回终点*/private int find(int[] parent, int node) {while (parent[node] > 0) {// 已经添加了边node = parent[node];// 以检索到的终点为新的起点继续检索}return node;}
}

测试实例

package Kruskal;import Kruskal.Kruskal.Edge;public class KruskalDemo {public static void main(String[] args) {int MAX_WEIGHT = Integer.MAX_VALUE;//定义无效权值int[][] Matrix = new int[9][];    //邻接矩阵//邻接矩阵初始化Matrix[0] = new int[] {0,10,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,11,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT};Matrix[1] = new int[] {10,0,18,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,16,MAX_WEIGHT,12};Matrix[2] = new int[] {MAX_WEIGHT,MAX_WEIGHT,0,22,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,8};Matrix[3] = new int[] {MAX_WEIGHT,MAX_WEIGHT,22,0,20,MAX_WEIGHT,24,16,21};Matrix[4] = new int[] {MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,20,0,26,MAX_WEIGHT,7,MAX_WEIGHT};Matrix[5] = new int[] {11,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,26,0,17,MAX_WEIGHT,MAX_WEIGHT};Matrix[6] = new int[] {MAX_WEIGHT,16,MAX_WEIGHT,26,MAX_WEIGHT,17,0,19,MAX_WEIGHT};Matrix[7] = new int[] {MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,16,7,MAX_WEIGHT,19,0,MAX_WEIGHT};Matrix[8] = new int[] {MAX_WEIGHT,12,8,21,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,MAX_WEIGHT,0};Kruskal k = new Kruskal(15);Edge[] e = k.createEdgeArray(Matrix);//可以打印e以检验是否排序,这里省略k.miniSpanTreeKruskal();}}

输出结果

连接起点:4,连接终点:7,权值为:7
连接起点:2,连接终点:8,权值为:8
连接起点:0,连接终点:1,权值为:10
连接起点:0,连接终点:5,权值为:11
连接起点:1,连接终点:8,权值为:12
连接起点:3,连接终点:7,权值为:16
连接起点:1,连接终点:6,权值为:16
边7构成了回路
边8构成了回路
连接起点:6,连接终点:7,权值为:19
边10构成了回路
边11构成了回路
边12构成了回路
边13构成了回路
边14构成了回路
sum:99

Java数据结构之克里斯卡尔算法(Kruskal算法)相关推荐

  1. 生成树的概念,最小生成树Prim算法 Kruskal算法

    求解最小生成树可以用Prim算法 Kruskal算法

  2. 基本数据结构(图: 基本结构,DFS,prim算法, kruskal算法)

    #include <iostream> using namespace std; //约定: //1. 图是由很多节点(VERTEX)构成的, 因此图结构是由一个VERTEX的链表构成的, ...

  3. 【数据结构】最小生成树 Prim算法 Kruskal算法

    最小生成树应用场景: 假设以下场景,有一块木板,板上钉上一些钉子,这些钉子可以由一些细绳连接起来.假设每个钉子可以通过一根或者多根细绳连接起来,那么一定存在这样得情况,即用最少的细绳把所有的钉子连接起 ...

  4. hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)

    还是畅通工程                                                                            Time Limit: 4000/2 ...

  5. 最小生成树 | Prim算法 Kruskal算法 |C语言

    一.Prim算法 MST收集的是顶点: 这里的dist[]是指结点V到最小生成树的最小距离: 当dist[V] = 0 时,表明结点V被收录到最小生成树中, 如果dist[W] != 0,表明结点W还 ...

  6. 最小生成树:Prim算法 /Kruskal算法(待深入复习理解)

    目录 1.各种概念和性质 2.prim算法 3.Kruskal算法 4.两种算法比较 1.各种概念和性质 无向图的生成树 最小生成树 构造最小生成树(利用MST性质) 2.prim算法 算法思想 算法 ...

  7. 最小生成树之 Prim算法 Kruskal算法

    1 描述 问题:修建一个连接各个小区与煤气供应站点之间的管道,使得造价成本最低,即构造一颗最小生成树.但是如何求解? 对应模型:树结构,生成树,最小生成树 2 prim算法实例 基本思想:在满足如下条 ...

  8. Python 数据结构与算法 —— Kruskal 算法

    1. 朴素版 # 查 def naive_find(C, u):while C[u] != u:u = C[u] # 一直传递下去,只要 C[u] != u,C 结构的定义在 naive_krusta ...

  9. java数据结构之单链表逆置算法

    单链表逆置算法1 设计思想:在链表类中新加成员方法getNode(int i),用来获取指定位置的节点,新建一个空单链表,将原链表的每个节点按照从后往前的顺序依次取出,再把节点的数据依次添加到新的链表 ...

最新文章

  1. Nagios+Centreon+Nrpe集成(二)
  2. SRS流媒体服务器——Edge集群搭建
  3. vue路由跳转报错解决
  4. MySQL之算术表达式、聚合函数及GROUP BY 与 HANVING 等函数的应用
  5. 动态规划求解装箱问题(洛谷P1049题题解,Java语言描述)
  6. android 音频配置文件,Android音频系统
  7. CentOS 7上搭建Spark 3.0.1 + Hadoop 3.2.1分布式集群
  8. jupyter新建文件_初学jupyter,运行,下载,上传导入文件
  9. Hyperledger Fabric教程(13)-- 集成CouchDB作为状态数据库
  10. centos 卸载docker_Docker (一) 安装
  11. 利用hdparm工具配合crontab使硬盘不用时休眠
  12. WinRAR5.40版 无广告
  13. DDR3/4 内存模组(SIMM DIMM RIMM ,SO-DIMM UDIMM RDIMM LRDIMM区别)
  14. AutoCAD2022下载安装教程
  15. 录制计算机网课,电脑怎么录制网络课程?简单专业的录制方法分享
  16. C#微信开放平台开发——1、序言
  17. 关于自动内存管理垃圾的产生和GC的应运而生。
  18. php只取时间的下士_闲来无聊,用python抓取天气信息,简单就是美啊
  19. ios 渐变透明背景_PS教程:通道抠图技巧换背景案例
  20. 深度学习入门之神经网络的学习

热门文章

  1. ccf练习-10. Maya历法
  2. keil中C51关键字code用法
  3. 运动控制学习 学习笔记(八)——定义和评价学习
  4. 寻 找 苍 凉 [转贴]
  5. Contacts和RawContacts的区别
  6. gradle的几个实用技巧让你爽歪歪
  7. matlab中的三维可视化实现
  8. Cinemachine(二)制作不会穿墙(会避开障碍物)的摄像头(Cinemachine Collider)
  9. 配置GOPROXY安装VScode的go语言开发插件
  10. 校招面试之数据库常见面试问题