39.克鲁斯卡尔(Kruskal)算法

一言

已知n个顶点,选n-1条最短的边,不可成环。

概述

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

  1. 要对边的权值进行排序;
  2. 不停加入新边且不能产生回路;

举个“栗”子

不妨从下面这个场景说起
在这里插入图片描述
郝乡长在光荣完成了德胜乡七个村子的修路任务之后,心情非常的好。因为他觉得之前悬而未决的交通巡检问题似乎也可以借鉴此前的宝贵经验。原来,得胜乡又七个集市(A-G),每逢过节都是人山人海,为了群众的安全,连贯的巡检是很必要的,可是如何设计连通七个集市的巡检路线呢?要短!要连贯!要高效!

图解

首先,不同的连接方式其权值总和也不同,如何找到最优解是关键。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
最后合龙,得到最优解
在这里插入图片描述
第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中。

分析

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

  1. 对图的所有边按照权值大小进行排序。
  2. 将边添加到最小生成树中时,怎么样判断是否形成了回路。
    对于1 ,很好解决,采用排序算法进行排序即可。
    对于2,可以记录顶点在"最小生成树"中的终点,顶点的终点是"在最小生成树中与它连通的最大顶点”。然后每次需要将一条边添加到最小生存树时判断该边的两个顶点的终点是否重合,重合的话则会构成回路。

如何判断回路

我们假定C->D->E->F连通,则此四个顶点实际都有终点:
C->F
D->F
E->F
F->F
终点的概念实际上就是避免二次访问,最小生成树要求每个点到另一个点都只有一种可能。以上例再加入CE边为例,在CE加入之前,从C到E已经有了CD->DE的方案,那么就不允许再引入CE这个方案。采用终点验证的方法实际上是把这个思路进行了归纳扩展。

代码实现

public class KruskalCase {private int edgeNum;//边的个数private char[]vertexs;//顶点数组private int[][]matrix;//邻接矩阵private static final int INF = Integer.MAX_VALUE;//使用INF表示两个顶点不能连同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  }};//创建KruskalCase 对象实例KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);//输出构建的kruskalCase.print();//        EData[] edges = kruskalCase.getEdges();
//        System.out.println("排序前:"+Arrays.toString(edges));//未排序
//        kruskalCase.sortEdges(edges);//排序
//        System.out.println("排序后:"+Arrays.toString(edges));//排序后kruskalCase.kruskal();}//构造器public KruskalCase(char[] vertexs, int[][] matrix) {//初始化顶点数和边个数int vlen = vertexs.length;//初始化顶点this.vertexs = vertexs;//初始化边this.matrix = matrix;//统计边的条数for (int i = 0; i < vlen; i++) {for (int j = i+1; j < vlen; j++) {if (this.matrix[i][j]!=INF){edgeNum++;}}}}//克鲁斯卡尔算法核心public void kruskal(){int index =0;//表示最后结果数组的索引int [] ends = new int[edgeNum];//用于保存“已有最小生成树”中的每个顶点在最小生成树中的终点//创建结果数组,保存最后的最小生成树EData [] rets = new EData[edgeNum];//获取图中所有的边的集合,一共有12条边EData[] edges = getEdges();System.out.println("图的边的集合:"+Arrays.toString(edges)+"。共"+edges.length+"条边。");//12//按照边的权值大小进行排序(从小到大)sortEdges(edges);//遍历edges数组,将边添加到最小生成树,判断准备加入的边是否构成回路,如果没有就加入rets,否则不能加入for (int i = 0; i < edgeNum; i++) {//获取第i条边的第1个顶点int p1 = getPosition(edges[i].start); // 比如边<E,F>, p1为4//获取第i条边的第2个顶点int p2 = getPosition(edges[i].end);   // 比如边<E,F>, p2为5//获取p1这个顶点在已有的最小生成树中的终点int m = getEnd(ends,p1);              //在上面的比如中,m=4//获取p2这个顶点在已有的最小生成树中的终点int n = getEnd(ends,p2);            //在上面的比如中,n=5//是否构成回路if (m!=n){//不构成回路//设置m在已有“最小生成树”中的终点。比如第一次:<E,F> [0,0,0,0,0,0,0,0,0,0,0,0] => [0,0,0,0,5,0,0,0,0,0,0,0]//对end数组的理解:第4位值为5,表示第4个顶点(E)的终点是第5个顶点(F)ends[m] = n;rets[index++] = edges[i];//边入选}}//统计并打印“最小生成树”,输出retsfor (int i = 0; i < index; i++) {System.out.println("最小生成树为=" + rets[i]);}}//打印邻接矩阵public void print(){System.out.println("邻接矩阵为:\n");for (int i = 0; i < vertexs.length; i++) {for (int j = 0; j < vertexs.length; j++) {System.out.printf("%12d\t",matrix[i][j]);}System.out.println();}}//边的排序(按权值),冒泡排序/*** @param edges 边的集合*/private void sortEdges(EData[]edges){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){//交换EData tmp =edges[j];edges[j] = edges[j+1];edges[j+1] = tmp;}}} }/*** @param ch 顶点的值, ‘A’,‘B’等* @return 返回ch顶点对应的下标,如果找不到返回-1*/private int getPosition(char ch){for (int i = 0; i < vertexs.length; i++) {if (vertexs[i]==ch)return i;}return -1;}/*** 获取图中的边,放到EData[]数组中,后面我们需要遍历该数组,通过matrix邻接矩阵来获取* EData[]形式 [['A','B',12],['B','F',7],......]* @return*/private EData[] getEdges(){int index = 0 ;EData[] edges = new EData[edgeNum];for (int i = 0; i < vertexs.length; i++) {for (int j = i+1; j < vertexs.length; j++) {if (matrix[i][j]!=INF){edges[index++] = new EData(vertexs[i],vertexs[j],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;}//重写toString,便于输出边@Overridepublic String toString() {return "EData{" +"<" + start +"," + end +"> = " + weight +'}';}
}

关注我,共同进步,每周至少一更。——Wayne

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/167547.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

位操作符^以及正负数在计算机中的存储

(数据是怎么在计算机中存储的)​ 正数和负数在内存中都是以补码的形式存储的&#xff0c;但不同的是正数的原码&#xff0c;补码&#xff0c;反码都是相同的&#xff0c;而负数的原码&#xff0c;补码和反码是不同的。 负数的原码&#xff0c;补码&#xff0c;反码之间存在什么…

git创建与合并分支

文章目录 创建与合并分支分支管理的概念实际操作 解决冲突分支管理策略Bug分支Feature分支多人协作 创建与合并分支 分支管理的概念 分支在实际中有什么用呢&#xff1f;假设你准备开发一个新功能&#xff0c;但是需要两周才能完成&#xff0c;第一周你写了50%的代码&#xf…

详细介绍如何使用Ipopt非线性求解器求解带约束的最优化问题

本文中将详细介绍如何使用Ipopt非线性求解器求解带约束的最优化问题&#xff0c;结合给出的带约束的最优化问题示例&#xff0c;给出相应的完整的C程序&#xff0c;并给出详细的解释和注释&#xff0c;以及编译规则等 一、Ipopt库的安装和测试 本部分内容在之前的文章《Ubuntu2…

在Windows下Edge浏览器OA发起流程问题

在Edge浏览器中发起流程 如上图所示&#xff0c;不能正常打开Excel&#xff0c;自动将Excel表格转为了PDF 怎么处理&#xff1f;还得使用IE浏览器来访问&#xff0c;但打开IE后又自动跳转到Edge&#xff0c;根本就不给使用&#xff0c;在Edge下使用IE模式也解决不了这个问题。…

【超级基础版】十进制与二进制的转换

目录 一、为什么是二进制&#xff1f; 二、二进制的加法和乘法 三、二进制向十进制转换 四、十进制整数向二进制转换 五、十进制小数向二进制小数的转换 六、八进制和十六进制的引入 一、为什么是二进制&#xff1f; 我们知道电脑的数据本质上是0和1&#xff0c;就是我们…

蓝桥杯中级题目之组合(c++)

系列文章目录 数位递增数_睡觉觉觉得的博客-CSDN博客拉线开关。_睡觉觉觉得的博客-CSDN博客蓝桥杯中级题目之数字组合&#xff08;c&#xff09;_睡觉觉觉得的博客-CSDN博客 文章目录 系列文章目录前言一、个人名片二、描述三、输入输出以及代码示例1.输入2.输出3.代码示例 总…

ArmSoM-W3之RK3588硬编解码MPP环境配置

1. 简介 瑞芯微提供的媒体处理软件平台&#xff08;Media Process Platform&#xff0c;简称 MPP&#xff09;是适用于瑞芯微芯片系列的 通用媒体处理软件平台。该平台对应用软件屏蔽了芯片相关的复杂底层处理&#xff0c;其目的是为了屏蔽不 同芯片的差异&#xff0c;为使用者…

电脑技巧:笔记本电脑网络不显示wifi列表解决办法

目录 1.WiFi功能被关闭 2.启用了飞行模式 3.WLAN连接被禁用 4.无线网卡驱动未安装 5.WLAN AutoConfig服务未启动 我的笔记本电脑连接wifi时&#xff0c;结果wifi列表中不显示任何的网络信息&#xff0c;这是怎么回事&#xff1f;要如何解决&#xff1f; 答&#xff1a;笔…

kaggle新赛:UBC卵巢癌亚型分类和异常检测大赛【图像分类】

赛题名称&#xff1a;UBC Ovarian Cancer Subtype Classification and Outlier Detection (UBC-OCEAN) 赛题链接&#xff1a;https://www.kaggle.com/competitions/UBC-OCEAN 赛题背景 卵巢癌是女性生殖系统最致命的癌症。目前&#xff0c;卵巢癌诊断依赖病理学家评估亚型。…

卷积神经网络CNN学习笔记-MaxPool2D函数解析

目录 1.函数签名:2.学习中的疑问3.代码 1.函数签名: torch.nn.MaxPool2d(kernel_size, strideNone, padding0, dilation1, return_indicesFalse, ceil_modeFalse) 2.学习中的疑问 Q:使用MaxPool2D池化时,当卷积核移动到某位置,该卷积核覆盖区域超过了输入尺寸时,MaxPool2D会…

【计算机网络笔记】TCP/IP参考模型基本概念,包括五层参考模型

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

EC11编码器编码使用

文章目录 前要原理脉冲与定位功能硬件设计 编程轮询模式定时器Encoder模式 结束语 前要 关于EC11编码器的了解可以参考两篇文章&#xff0c;比较详细&#xff0c;在此就不多介绍了&#xff1a; 一篇文章带你了解——EC11编码器&#xff08;关于硬件、原理图、上下拉等都有讲&…

Vue3踩坑指南

vue.config.ts不起作用 关于项目 项目使用的还是vue-cli搭建的&#xff0c;底层还是webpack&#xff0c;没有使用新的vite搭建。 踩坑1&#xff1a;vue.config.ts不起作用 我本着既然是vue3 ts的项目&#xff0c;那么为了规范&#xff0c;项目中所有的js文件都得替换成ts文…

idea的debug调试

目录 断点条件设置(condition) 断点表达式(evaluate expression) 断点回退(reset frame) 断点条件设置(condition) 条件断点&#xff0c;一般是满足我们设置的某个条件时&#xff0c;debug断点才会生效。这种条件断点设置&#xff0c;我们一般用在多重循环中。 这儿我们以li…

vue3脚手架搭建

一.安装 vue3.0 脚手架 如果之前安装了2.0的脚手架&#xff0c;要先卸载掉&#xff0c;输入&#xff1a; npm uninstall vue-cli -g 进行全局卸载 1.安装node.js&#xff08;npm&#xff09; node.js&#xff1a;简单的说 Node.js 就是运行在服务端的 JavaScript。Node.js 是…

Windows 安装 Java

1. 安装 JDK 从 Oracle 的官网下载的 JDK&#xff0c;例如 JDK 21 双击下载得到的 msi 文件&#xff0c;开始安装 JDK 选择要安装的文件路径&#xff08;我一般都默认&#xff09;&#xff1a; 等待安装&#xff1a; 安装完成&#xff1a; 2. 验证是否安装成功 2.1. 打开 cmd…

antd vue 组件 使用下拉框的层级来显示后面的输入框

效果图&#xff1a; 代码&#xff1a; HTML: <dir><a-row><a-col :span"4"><a-form-model-item label"审批层级" ><a-selectplaceholder"请选择审批层级"v-model"form.PlatformPurchaseApproveLevel"cha…

Linux笔记之diff和vimdiff

Linux笔记之diff和vimdiff code review! 文章目录 Linux笔记之diff和vimdiff一.diff1.1.使用diff比较文件夹1.2.使用diff比较文件1.4.colordiff——带颜色输出差异 二.vimdiff2.1.vimdiff颜色差异2.2.vimfiff调整栏宽2.3.修改颜色变谈&#xff0c;使代码可以看清楚2.4.vimdif…

FreeRTOS深入教程(任务的引入及栈的作用)

文章目录 前言一、任务的引入二、深入理解C语言函数的调用1.ARM架构2.基础汇编指令3.函数运行流程分析 三.保存现场的几种情况1.函数调用2.中断处理3.任务切换 总结 前言 本篇文章开始带大家深入学习FreeRTOS&#xff0c;带大家学习什么是任务&#xff0c;并且深入学习栈的作用…

AWS SAA-C03考试知识点整理

S3&#xff1a; 不用于数据库功能 分类&#xff1a; S3 Standard &#xff1a;以便频繁访问 S3 Standard-IA 或 S3 One Zone-IA &#xff1a; 不经常访问的数据 Glacier&#xff1a; 最低的成本归档数据 S3 Intelligent-Tiering智能分层 &#xff1a;存储具有不断变化或未知访问…