图——最小生成树实现(Kruskal算法,prime算法)

目录

预备知识:

最小生成树概念:

Kruskal算法:

代码实现如下:

测试:

Prime算法 :

代码实现如下:

测试:

结语:


预备知识:

连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一 对顶点 都是连通的,则称此图为连通图。

生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。

并查集:

由于本文章重点不在讲述并查集,故下面我简单描述并查集的作用,各种方法,源码如下。

并查集的作用:可以将一个数组中的元素分为多个小组的数据结构。

方法:

findRoot(x):查找x的根。

union(int x1, int x2):合并x1和x2。

isSameSet(int x1, int x2):判断两个数字 是不是在同一个集合当中。

import java.util.Arrays;public class UnionFindSet {private int[] elem;//底层是数组public UnionFindSet(int n){this.elem = new int[n];Arrays.fill(elem,-1);//整体初始化为-1:代表根}/*** 查找x的根* @param x* @return*/public int findRoot(int x){if(x < 0){throw new IndexOutOfBoundsException("数据不合法");}while(elem[x] >= 0){x = elem[x];}return x;}/*** 合并x1和x2* @param x1* @param x2*/public void union(int x1,int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index1 == index2){//说明x1和x2的根是相同的,不需要进行合并return;}elem[index1] = elem[index1] + elem[index2];elem[index2] = index1;//将x2合并到x1}/*** 判断两个数字是不是在同一个集合当中* @param x1* @param x2* @return*/public boolean isSameSet(int x1,int x2){int index1 = findRoot(x1);int index2 = findRoot(x2);if(index1 == index2){return true;}else{return false;}} 
}

最小生成树概念:

连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树 就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。

若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。因此构造最小生成树的准则有三 条:

(1) 只能使用图中的边来构造最小生成树。

(2) 只能使用恰好n-1条边来连接图中的n个顶点。

(3) 选用的n-1条边不能构成回路。

构造最小生成树的方法:Kruskal算法和Prim算法。这两个算法都采用了逐步求解的贪心策略。

贪心算法:是指在问题求解时,总是做出当前看起来最好的选择。也就是说贪心算法做出的不是整体最优的选择,而是某种意义上的局部最优解。贪心算法不是对所有的问题都能得到整体最优解。

Kruskal算法:

Kruskal算法采用全局贪心的策略,其步骤如下:

任给一个有n个顶点的连通网络N={V,E}。

(1)首先构造一个由这n个顶点组成、不含任何边的图G={V,NULL},其中每个顶点自成一个连通分量。

(2)其次不断从E中取出权值最小的一条边(若有多条任取其一),若该边的两个顶点来自不同的连通分量(若相同则不加因为会生成环),则将此边加入到G中。

(3)如此重复,直到所有顶点在同一个连通分量上为止。

核心:每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树。

 具体过程如下图所示:按照abc..的循序,箭头为当前要操作的位置(不一定能添加,黑色为可添加)。

  

代码实现如下:

先构造关于Edge的小根堆,由于是自定义类,故要自己实现一个比较器Comparator。

1. 定义优先级队列存储边构建小根堆 跟进权重进行比较。

2. 把矩阵当中的边全部入队列。

3. 定义并查集判断将来两条边是不是在一个集合(避免构成环)。

4. 由于篇幅有限matrix之类的前文实现过这里不在实现有需要的友友可以前往图的概念

static class Edge{public int srcIndex;public int destIndex;public int weight;public Edge(int srcIndex,int destIndex,int weight){this.srcIndex = srcIndex;this.destIndex = destIndex;this.weight = weight;}}public int kruskal(GraphByMatrix minTree){//1. 定义优先级队列 存储边 构建小根堆 跟进权重进行比较PriorityQueue<Edge> minHeap = new PriorityQueue<>(new Comparator<Edge>(){@Overridepublic int compare(Edge o1,Edge o2){return o1.weight - o2.weight;}});int n = matrix.length;//2. 把矩阵当中的边全部入队列for(int i = 0;i < n;i++){for(int j = 0;j < n;j++){//因为是无向图,所以只入一半就可以 i < j 即可if(i < j && matrix[i][j] != Integer.MAX_VALUE){Edge edge = new Edge(i,j,matrix[i][j]);minHeap.offer(edge);}}}//3、最后整个的权重int totalWeight = 0;int size= 0;//4.定义并查集 判断将来两条边 是不是在一个集合UnionFindSet ufs = new UnionFindSet(n);//5. 出优先级队列的n-1条边while(size < n-1 &&!minHeap.isEmpty()){Edge min  = minHeap.poll();int srcIndex = min.srcIndex;int destIndex = min.destIndex;//判断是不在在同一个集合当中,在一个集合 就不能添加if(!ufs.isSameSet(srcIndex,destIndex)){//打印选出的边System.out.println("选择的边: "+ arrayV[srcIndex] + "-> "+ arrayV[destIndex] + ":"+matrix[srcIndex][destIndex]);?minTree.addEdgeUseIndex(srcIndex,destIndex,min.weight);totalWeight += min.weight;//添加完成之后,说明 可以 合并到同一个集合ufs.union(srcIndex,destIndex);size++;}}//如果是 选出n-1条边,否则就说明不是连通图if(size == n-1){return totalWeight;}//不是连通图, 可能选不出n-1条边  假设一个图中,有其他的顶点独立着return -1;}private void addEdgeUseIndex(int srcIndex,int destIndex,int weight) {matrix[srcIndex][destIndex] = weight;//如果是无向图 那么相反的位置 也同样需要置为空if(!isDirect) {matrix[destIndex][srcIndex] = weight;}}

测试:

测试代码对应的图:

测试代码 :

public static void main(String[] args) {testGraphMinTreeKruskal();}public static void testGraphMinTreeKruskal() {String str = "abcdefghi";char[] array =str.toCharArray();GraphByMatrix g = new GraphByMatrix(str.length(),false);g.initArrayV(array);g.addEdge('a', 'b', 4);g.addEdge('a', 'h', 8);//g.addEdge('a', 'h', 9);g.addEdge('b', 'c', 8);g.addEdge('b', 'h', 11);g.addEdge('c', 'i', 2);g.addEdge('c', 'f', 4);g.addEdge('c', 'd', 7);g.addEdge('d', 'f', 14);g.addEdge('d', 'e', 9);g.addEdge('e', 'f', 10);g.addEdge('f', 'g', 2);g.addEdge('g', 'h', 1);g.addEdge('g', 'i', 6);g.addEdge('h', 'i', 7);GraphByMatrix  kminTree = new GraphByMatrix(str.length(),false);System.out.println(g.kruskal(kminTree));kminTree.printGraph();}

效果:

显然正确💯

Prime算法 :

Primel算法采用局部贪心的策略,其步骤如下:

按照字母顺序abc....看。

代码实现如下:

由于是局部贪心用两个Set,那么天然就不会有环,故prime可以不用并查集。

1. 先获取当前顶点的下标。

2. 定义一个X集合,把当前的起点下标存进去。

3. 定义一个Y集合,存储目标顶点的元素。

4. 除了刚刚的起点,其他的顶点需要放到Y。

5. 从X集合中的点到Y集合的点中,连接的边中找出最小值放到优先级队列。

6. 把当前顶点连接出去的所有的边放入队列。

7.把这次的目标点,添加到X集合,变成了起点记得把之前的目标点,从Y集合删除掉。

8.遍历刚刚添加的新起点destIndex,连接出去的所有边,再次添加到优先级队列。

public int prim(GraphByMatrix minTree,char chV){//1. 先获取当前顶点的下标int srcIndex = getIndexOfV(chV);int n = arrayV.length;//2. 定义一个X集合,把当前的起点下标存进去Set<Integer> setX = new HashSet<>();//3. 定义一个Y集合,存储目标顶点的元素Set<Integer> setY = new HashSet<>();setX.add(srcIndex);//4. 除了刚刚的起点,其他的顶点需要放到Y集合for(int i = 0;i < n;i++){if(i != srcIndex){setY.add(i);}}//5. 从X集合中的点到Y集合的点中,连接的边中找出最小值放到优先级队列PriorityQueue<Edge> minHeap = new PriorityQueue<>(new Comparator<Edge>(){@Overridepublic int compare(Edge o1,Edge o2){return o1.weight - o2.weight;}});//6. 把当前顶点连接出去的所有的边放入队列for(int i = 0;i < n;i++){if(matrix[srcIndex][i] != Integer.MAX_VALUE){minHeap.offer(new Edge(srcIndex,i,matrix[srcIndex][i]));}}int size = 0;int totalWeight = 0;while(size < n - 1 && !minHeap.isEmpty()){//7. 取出队列中的第一条边Edge min = minHeap.poll();int srcI = min.srcIndex;int destI = min.destIndex;//起始点本身就在X集合,所以这里只需要判断目标点即可if(setX.contains(destI)){//包含}else{//8. 直接将该边 放入最小生成树minTree.addEdgeUseIndex(srcI,destI,min.weight);//9. 每选一条边 就打印一条语句System.out.println("选择的边: "+ arrayV[srcI] + "-> "+ arrayV[destI] + ":"+matrix[srcI][destI]);size++;totalWeight += min.weight;//10.把这次的目标点,添加到X集合,变成了起点setX.add(destI);//11.记得把之前的目标点,从Y集合删除掉setY.remove(destI);//12. 遍历刚刚添加的新起点destIndex,连接出去的所有边,再次添加到优先级队列for(int i = 0;i < n;i++){// 13. !setX.contains(i) 判断目标点不能再X这个集合 例如: a->b 就包含了b->aif(matrix[destI][i] != Integer.MAX_VALUE && !setX.contains(i)){minHeap.offer(new Edge(destI,i,matrix[destI][i]));}}}}if(size == n-1){return totalWeight;}else{return -1;}}

测试:

测试对应的图:

测试代码 :

public static void main(String[] args) {testGraphMinTreePrime();}public static void testGraphMinTreePrime() {String str = "abcdefghi";char[] array = str.toCharArray();GraphByMatrix g = new GraphByMatrix(str.length(), false);g.initArrayV(array);g.addEdge('a', 'b', 4);g.addEdge('a', 'h', 8);//g.addEdge('a', 'h', 9);g.addEdge('b', 'c', 8);g.addEdge('b', 'h', 11);g.addEdge('c', 'i', 2);g.addEdge('c', 'f', 4);g.addEdge('c', 'd', 7);g.addEdge('d', 'f', 14);g.addEdge('d', 'e', 9);g.addEdge('e', 'f', 10);g.addEdge('f', 'g', 2);g.addEdge('g', 'h', 1);g.addEdge('g', 'i', 6);g.addEdge('h', 'i', 7);GraphByMatrix primTree = new GraphByMatrix(str.length(), false);System.out.println(g.prim(primTree, 'a'));primTree.printGraph();}

效果:

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

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

相关文章

Sora的第一波受害者出现了。

不知道大家最近除了被Sora刷屏之外&#xff0c;有没有被这张图刷屏 我只能说网友太强大了 说实话&#xff0c;我进入舟老师的直播间&#xff0c;每次都是还有3分钟下播&#xff0c;还有6单就拍完 但是10分钟后还在激情逼单&#xff0c;6单之后还有6单 也许在营销学上&#x…

深入理解nginx的动态变量机制【上】

目录 1. 概述2. 动态变量的分类2.1 按照变量名的确定性来分类2.2 按照变量声明的来源分类2.3 按照是否可以变更分类2.4 按照是否可以缓存分类2.5 按照变量的索引方式分类 3. 变量的使用3.1 声明一个变量3.1.1 支撑变量声明的nginx关键结构体3.1.2 在配置文件中声明3.1.3 在http…

C++笔记:OOP三大特性之多态

前言 本博客中的代码和解释都是在VS2019下的x86程序中进行的&#xff0c;涉及的指针都是 4 字节&#xff0c;如果要其他平台下测试&#xff0c;部分代码需要改动。比如&#xff1a;如果是x64程序&#xff0c;则需要考虑指针是8bytes问题等等。 文章目录 前言一、多态的概念二、…

【C++初阶】系统实现日期类

目录 一.运算符重载实现各个接口 1.小于 (d1)<> 2.等于 (d1d2) 3.小于等于&#xff08;d1<d2&#xff09; 4.大于&#xff08;d1>d2&#xff09; 5.大于等于&#xff08;d1>d2&#xff09; 6.不等于&#xff08;d1!d2&#xff09; 7.日期天数 (1) 算…

mac图片怎么转换格式jpg?四种高效方法助你轻松搞定JPG格式

mac图片怎么转换格式jpg&#xff1f;在数字时代&#xff0c;图片格式的转换成为了我们日常操作中的一项基本技能。特别是在使用Mac操作系统的用户中&#xff0c;如何将图片转换为JPG格式成为了一个热门话题。本文将为你详细介绍四种简单实用的方法&#xff0c;帮助你在Mac上轻松…

测试基础1:伟大航路哟呼(Linux基础、mysql基础)

1 测试流程和方法 软件测试定义&#xff1a; 从方式上看&#xff1a;包含人工测试、自动化测试 从方法上看&#xff1a;运行程序或系统和测定程序或系统的过程 从目的上看&#xff1a;包括找bug和找bug出现的原因 软件测试的原则&#xff1a;功能性、可靠性、易用性、效率性…

一、网络基础知识

1、IP地址和端口号 1.1、IP地址 定义&#xff1a;用于在网络中唯一标识设备的地址。格式&#xff1a;通常由四个数字组成&#xff0c;以点分十进制表示&#xff0c;例如&#xff1a;192.168.0.1。(IPv4)作用&#xff1a;允许网络中的设备相互通信&#xff0c;通过IP地址可以定…

Python 数据可视化之密度散点图 Density Scatter Plot

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 密度散点图&#xff08;Density Scatter Plot&#xff09;&#xff0c;也称为密度点图或核密度估计散点图&#xff0c;是一种数据可视化技术&#xff0c;主要用于展示大量数据点在二维平面上的分布情况…

Swift基础知识:24.Swift可选链

在 Swift 中&#xff0c;可选链&#xff08;Optional Chaining&#xff09;是一种用于调用可选类型属性、方法或下标的安全方式。可选链允许我们在调用链中的任何一个属性、方法或下标返回 nil 时&#xff0c;整个调用链仍然可以继续执行&#xff0c;而不会因为其中的任何一个可…

一样的代码不同项目跳转页面报404的解决办法

今天收到实施反馈的一个问题&#xff0c;点项目名称跳转项目详情页面时&#xff0c;有的页面跳转显示正常&#xff0c;有的页面跳转报404错误。错误如下&#xff1a; 发现报错的项目都有一个共性就是有特殊字符“[ ]” , 解决的办法就是把带有特殊字符的字段 用 encodeURI()…

Java SE 入门到精通—4.抽象类与接口【Java】

抽象类 同接口一样&#xff0c;用来约束子类&#xff0c;限制子类必须拥有某些方法&#xff0c;比普通类多了个抽象方法&#xff0c;用抽象方法该类必为抽象类 概念 没有具体的对象&#xff0c;具体的方法的一个类 abstract关键字声明为抽象类/方法 一个类中有抽象方法则该…

统计前端传过来的Req的非空属性个数的工具类

背景 日常开发中&#xff0c;我们通常会根据前端传过来的实体类的属性个数去做逻辑判断&#xff0c;下面的是判断属性个数的工具类。 工具类 public static Integer nonNullFieldCount(Req req) {if (req null) {return 0;}int nonNullFieldCount 0;Field[] fields req.ge…

【Django】Django自定义后台表单——对一个关联外键对象同时添加多个内容

以官方文档为例&#xff1a; 一个投票问题包含多个选项&#xff0c;基本的表单设计只能一个选项一个选项添加&#xff0c;效率较低&#xff0c;如何在表单设计中一次性添加多个关联选项&#xff1f; 示例代码&#xff1a; from django.contrib import adminfrom .models impo…

Java中的关键字有哪些?它们各自的作用是什么?请详细说明?Java中的访问修饰符有哪些?它们的访问权限是怎样的?

1、Java中的关键字有哪些&#xff1f;它们各自的作用是什么&#xff1f;请详细说明&#xff1f; Java中的关键字是预先定义好的&#xff0c;具有特殊含义的标识符&#xff0c;用于表示数据类型、程序结构或控制流程等。以下是Java中的一些常用关键字及其作用&#xff1a; abs…

【软件架构】02-复杂度来源

1、性能 1&#xff09;单机 受限于主机的CPU、网络、磁盘读写速度等影响 在多线程的互斥性、并发中的同步数据状态等&#xff1b; 扩展&#xff1a;硬件资源、增大线程池 2&#xff09;集群 微服务化拆分&#xff0c;导致调用链过长&#xff0c;网络传输的消耗过多。 集…

嵌入式Qt 计算器核心算法_3

一.后缀表达式实现算数运算思路 二.算法实现 #include "QCalculatorDec.h"QCalculatorDec::QCalculatorDec() {m_exp "";m_result ""; }QCalculatorDec::~QCalculatorDec() {}bool QCalculatorDec::isDigitOrDot(QChar c) {return ((0 < c)…

基于SpringBoot的景区旅游管理系统

项目介绍 本期给大家介绍一个 景区旅游管理 系统.。主要模块有首页&#xff0c;旅游路线&#xff0c;旅行攻略&#xff0c;在线预定。管理员可以登录管理后台对用户进行管理&#xff0c;可以添加酒店&#xff0c;景区&#xff0c;攻略&#xff0c;路线等信息。整体完成度比较高…

一文搞懂match、match_phrase与match_phrase_prefix的检索过程

一、在开始之前&#xff0c;完成数据准备&#xff1a; # 创建映射 PUT /tehero_index {"settings": {"index": {"number_of_shards": 1,"number_of_replicas": 1}},"mappings": {"_doc": {"dynamic": …

探索气膜球幕影院:未来的电影体验

气膜球幕影院作为一种新兴的电影放映方式&#xff0c;正逐渐成为人们关注的焦点。它采用了充气式膜结构&#xff0c;可以为观众带来 360 度全景的观影体验&#xff0c;让人仿佛置身于电影之中。本文将介绍气膜球幕影院的特点、技术原理以及未来的发展前景。 传说在古代&#x…

Linux系统运维命令:使用 tail,grep组合命令(包括wc,sort,awk,sed等),可以方便的查阅和操作正在改变的日志文件的具体内容

一、命令介绍 1、tail命令 tail命令是Linux系统中常用的命令之一&#xff0c;用于查看文件的末尾内容。它具有许多有用的选项&#xff0c;可以帮助用户轻松地查找并显示文件中的信息。 它默认显示文件的最后10行&#xff0c;但可以通过各种选项来定制输出的行数、字节数等。ta…