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.mzph.cn/news/114618.shtml

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

相关文章

一百九十一、Flume——Flume配置文件各参数含义(持续完善中)

一、目的 在实际项目的开发过程中&#xff0c;不同Kafka主题的数据规模、数据频率&#xff0c;需要配置不同的Flume参数&#xff0c;而这一切的调试、配置工作&#xff0c;都要建立在对Flume配置文件各参数含义的基础上 二、Flume各参数及其含义 &#xff08;一&#xff09;…

集成学习方法(随机森林和AdaBoost)

释义 集成学习很好的避免了单一学习模型带来的过拟合问题 根据个体学习器的生成方式&#xff0c;目前的集成学习方法大致可分为两大类&#xff1a; Bagging(个体学习器间不存在强依赖关系、可同时生成的并行化方法) 流行版本&#xff1a;随机森林(random forest)Boosting(个体…

springboot缓存篇之mybatis一级缓存和二级缓存

前言 相信很多人都用过mybatis&#xff0c;这篇文章主要是介绍mybatis的缓存&#xff0c;了解一下mybatis缓存是如何实现&#xff0c;以及它在实际中的应用 一级缓存 什么是mybatis一级缓存&#xff1f;我们先看一个例子&#xff1a; GetMapping("/list") public…

租用服务器后需要注意什么呢

租用服务器后需要注意什么呢 1、从IDC服务商中接收到服务器时&#xff0c;需要对服务器的各项性能进行测试确认&#xff0c;并做好记录以便对服务器的性能做到心中有数。 2、在服务器租用交接时&#xff0c;要了解服务器的安全设置情况&#xff0c;对服务器安全技术方面不了解…

深度学习硬件配置推荐(kaggle学习)

目录 1. 基础推荐2. GPU显存与内存是一个1:4的配比&#xff1f;3. deep learning 入门和kaggle比赛4. 有些 Kaggle 比赛数据集很大&#xff0c;可能需要更多的 GPU 显存&#xff0c;请推荐显存4. GDDR6和HBM25. HDD 或 SATA SSD 1. 基础推荐 假设您作为一个深度学习入门学者的…

【Mysql】B+树索引的使用(七)

前言 每个索引都对应一棵 B 树&#xff0c; B 树分为多层&#xff0c;最下边一层是叶子节点&#xff0c;其余的是内节点&#xff08;非叶子节点&#xff09;。所有用户记录都存储在 B 树的叶子节点&#xff0c;所有目录项记录都存储在内节点。 InnoDB 存储引擎会自动为主键&am…

Node学习笔记之包管理工具

一、概念介绍 1.1 包是什么 『包』英文单词是package &#xff0c;代表了一组特定功能的源码集合 1.2 包管理工具 管理『包』的应用软件&#xff0c;可以对「包」进行 下载安装 &#xff0c; 更新 &#xff0c; 删除 &#xff0c; 上传 等操作 借助包管理工具&#xff0c;可…

推理引擎之模型压缩浅析

目录 前言1. 模型压缩架构和流程介绍2. 低比特量化原理2.1 量化基础介绍2.2 量化方法2.3 量化算法原理2.4 讨论 3. 感知量化训练QAT原理3.1 QAT原理3.2 量化算子插入3.3 QAT训练流程3.4 QAT衍生研究3.5 讨论 4. 训练后量化PTQ4.1 动态PTQ4.2 静态PTQ4.3 KL散度实现静态PTQ4.4 量…

最详细STM32,cubeMX 定时器

这篇文章将详细介绍 STM32,cubeMX 定时器的配置和使用。 文章目录 前言一、定时器基础知识二、cubeMX 配置三、定时时长四、自动生成代码讲解五、实验程序总结 前言 实验开发板&#xff1a;STM32F103C8T6。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff…

无人机UAV目标检测与跟踪(代码+数据)

前言 近年来&#xff0c;随着无人机的自主性、灵活性和广泛的应用领域&#xff0c;它们在广泛的消费通讯和网络领域迅速发展。无人机应用提供了可能的民用和公共领域应用&#xff0c;其中可以使用单个或多个无人机。与此同时&#xff0c;我们也需要意识到无人机侵入对空域安全…

牛客:NC59 矩阵的最小路径和

牛客&#xff1a;NC59 矩阵的最小路径和 文章目录 牛客&#xff1a;NC59 矩阵的最小路径和题目描述题解思路题解代码 题目描述 题解思路 动态规划&#xff0c;递推公式&#xff1a;matrix[i][j] min(matrix[i-1][j], matrix[i][j-1]) 题解代码 func minPathSum( matrix [][…

【数据科学赛】2023全球智能汽车AI挑战赛 #¥95000 #LLM文档问答 #视频理解

CompHub[1] 最新的比赛会第一时间在群里通知&#xff0c;欢迎加群交流比赛经验&#xff01;&#xff08;公众号回复“加群”即可&#xff09; 以下内容由AI辅助生成&#xff0c;可能存在错误&#xff0c;可进入比赛主页[2]查看更多(文末阅读原文) 比赛主办方 吉利汽车集团、阿…

智能指针

unique_ptr 为什么引入智能指针unique_ptr 普通指针的不足 内存泄漏&#xff1a;最常见的00问题是忘记释放分配给指针的内存&#xff0c;导致内存泄漏。这通常发生在异常情况或程序员忘记在不再需要内存时释放它。悬空指针&#xff1a;当对象被删除&#xff0c;但指针未被重…

【C++】:类和对象(中)之拷贝构造函数+赋值运算符重载

拷贝构造函数 概念 在现实生活中&#xff0c;可能存在一个与你一样的自己&#xff0c;我们称其为双胞胎 那在创建对象时&#xff0c;可否创建一个与已存在对象一某一样的新对象呢&#xff1f; 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用…

怎么让英文大语言模型支持中文?--构建中文tokenization--继续预训练--指令微调

1 构建中文tokenization 参考链接&#xff1a;https://zhuanlan.zhihu.com/p/639144223 1.1 为什么需要 构建中文tokenization&#xff1f; 原始的llama模型对中文的支持不太友好&#xff0c;接下来本文将讲解如何去扩充vocab里面的词以对中文进行token化。 1.2 如何对 原始数…

单独使用logback作为日志

文章目录 引入logback的依赖logback.xml测试 具体配置&#xff0c;可参看&#xff1a;LogBack日志学习&#xff08;全面&#xff09; logback使用实战 log4j使用实战 log4j2使用实战 Java日志系统之Logback Java日志系统之Slf4j Java日志系统之Log4j 引入logback的依赖 …

JAVA观察者模式-案例

Java观察者模式 import java.util.ArrayList; import java.util.List; // 主题接口&#xff0c;定义了添加、删除观察者等方法 interface Subject { void register(Observer observer); void unregister(Observer observer); void notifyObservers(String message); …

FPGA的斐波那契数列Fibonacci设计verilog,代码和视频

名称&#xff1a;斐波那契数列Fibonacci设计verilog 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 设计一个产生斐波那契数列&#xff08;也叫黄金分割数列&#xff09;的硬件电路: 斐波那契数列中每个数为其相邻前两个数的和:即FNFN1FN2,(数列…

探索数据结构世界之排序篇章(超级详细,你想看的都有)

-文章开头必看 1.&#xff01;&#xff01;&#xff01;本文排序默认都是排升序 2.排序是否稳定值指指排完序之后相同数的相对位置是否改变 3.代码相关解释我都写在注释中了&#xff0c;方便对照着看 1.插入排序1.1直接插入排序1.2希尔排序1.2.1单趟1.2.2多趟基础版——排完一…

Python:函数篇(每周练习)

编程题&#xff1a; Python第四章作业&#xff08;初级&#xff09; (educoder.net) 题一&#xff1a;无参无返回值函数 def print_hi_human(): # 函数名用小写字母print("人类&#xff0c;你好&#xff01;")if __name__ __main__:print_hi_human() 题二&#…