使用Krukal算法解决图的最小生成树问题

Kruskal 算法

Kruskal算法是一种用于寻找连通图中最小生成树的算法。最小生成树是一个包含图中所有顶点的树,且边权重之和最小。Kruskal算法是一种贪心算法,它的基本思想是:每次选择边权重最小的边来扩展树,直到树包含所有的顶点。

以下是Kruskal算法的步骤:

  1. 按边权重从小到大排序。
  2. 初始时,每个顶点都是一个单独的树。
  3. 选择边权重最小的边,如果这条边不形成环,将其添加到树中。
  4. 重复步骤3,直到所有顶点都连接到一个树上。

为了确保不形成环,Kruskal算法使用了一种称为“割点”的优化技术。一个割点是这样一个顶点:删除它之后,图将分为两个或多个连通分量。在Kruskal算法中,我们首先考虑那些包含多个连通分量的顶点,并将它们作为候选的割点。然后,我们选择边权重最小的边,并确保它不连接同一个连通分量中的两个顶点。

Kruskal算法的时间复杂度是O(E log V),其中E是边的数量,V是顶点的数量。这个时间复杂度是由排序步骤决定的,因为我们需要按边权重对所有边进行排序。

Kruskal算法适用于边权重非负的连通图,并且可以用来解决一些其他的优化问题,如求解单源最短路径问题(当图中没有负权重边时)。在实际应用中,Kruskal算法常用于电信网络、计算机网络和其他需要高效传输的系统中,以确定成本最低的基础设施布局。

img

根据图片构成出图的邻接矩阵,并且根据邻接矩阵得到路径数组,写出代码如下:

并且使用sort函数,让路径数组按从小到大排序

#include<stdio.h>
#include<iostream>
#include<vector>
#include<queue>
#include <algorithm> 
using namespace std;

typedef struct Path{        //定义路径结构体 
	int start;              //起点 
	int terminal;           //终点 
	int values;             //权值 
}Path;

int g[9][9];               //路径邻接矩阵 
void newGraph() {
	for(int i = 0; i < 9; i++) {
		for(int j = 0; j < 9; j++) {
			g[i][j] = INT32_MAX;      //最大值意味无路径 
		}
	}
	g[0][1] = 3;           //为邻接矩阵赋值 
	g[1][0] = 3;
	g[0][5] = 4;
	g[5][0] = 4;
	g[1][2] = 8;
	g[2][1] = 8;
	g[1][8] = 5;
	g[8][1] = 5;
	g[1][6] = 6;
	g[6][1] = 6;
	g[2][8] = 2;
	g[8][2] = 2;
	g[2][3] = 12;
	g[3][2] = 12;
	g[3][8] = 11;
	g[8][3] = 11;
	g[3][6] = 14;
	g[6][3] = 14;
	g[3][7] = 6;
	g[7][3] = 6;
	g[3][4] = 10;
	g[4][3] = 10;
	g[4][5] = 18;
	g[5][4] = 18;
	g[4][7] = 1;
	g[7][4] = 1;
	g[5][6] = 7;
	g[6][5] = 7;
	g[6][7] = 9;
	g[7][6] = 9;
	for(int i = 0; i < 9; i++) {                //输出邻接矩阵验证 
			for(int j = 0; j < 9; j++) {
				if(g[i][j] == INT32_MAX) cout << "x ";
				else cout << g[i][j] <<" ";
			}
			cout<<endl;
		}
		cout<<endl;
}

vector<Path> path;                              //存放路径的数组 
void addPath() {
	for(int i = 0; i < 9; i++) {           
		for(int j = 0; j < 9; j++) {
			Path cur_path;
			if(g[i][j] != INT32_MAX) {          //倘若有路径 
				cur_path.start = i;             //将邻接矩阵中的路径转化为路径结构体,添加到path中 
				cur_path.terminal = j;
				cur_path.values = g[i][j];
				path.push_back(cur_path);
			}
		}
	}
	sort(path.begin(), path.end(), [](const Path& a, const Path& b) {
	    return a.values < b.values; // 根据权值升序排序
	});
	for(Path i: path) {             //按顺序输出路径数组 
		cout << i.start << "->" << i.terminal << ":" << i.values;
		cout<<endl;
	}
}

运行结果验证我们的代码没有错误。

在这里插入图片描述
在这里插入图片描述

这里路径重复的问题在后文会出现。。

使用队列,将路径逆向添加到队列中。

所以数组排序应该改为升序。

queue<Path> queuePath;         //路径优先队列
...
	sort(path.begin(), path.end(), [](const Path& a, const Path& b) {
	    return a.values < b.values; // 根据权值降序排序
	});
	for(Path i: path) {             //按逆序添加路径到优先队列中 
		queuePath.push(i);
	}
...

因为邻接矩阵中重复定义了同一条路径,如g【i】【j】和g【j】【i】是同一条路径,所以再加入路径数组(队列)中,会存在重复定义,并且本文不会添加去重部分,所以需要修改邻接矩阵中只定义一条路径即可。

后面就是Kruskal算法代码加上验证是否闭环的函数,如下:

vector<int> ans;                   //存放构成生成树的结点 
int path_sum;                      //记录总路径和 
bool closedTree(int i, int j) {
	bool i_exited = false;
	bool j_exited = false;
	
	for(int u : ans) {
		if(u == i) i_exited = true;
		if(u == j) i_exited = true;
	}
	if(i_exited && j_exited) return false;
	else return true;
}

void Kruskal(){
	
	while(!queuePath.empty()) {
		Path choice = queuePath.front();
		if(closedTree(choice.start, choice.terminal)) {
			ans.push_back(choice.start);         //将路径的起点和终点添加到结点数组ans中 
			ans.push_back(choice.terminal);
			path_sum += choice.values;
			cout << choice.start << "," << choice.terminal <<")=" << choice.values << endl;
		}
		queuePath.pop();
	} 
}

在这里插入图片描述

运行结果如图,这里路径起点和终点我并没有重复要求,因为不想添加函数去重,所以这里的起点终点就是邻接矩阵中的路径坐标。

总代码如下:

#include<stdio.h>
#include<iostream>
#include<vector>
#include<queue>
#include <algorithm> 
using namespace std;

typedef struct Path{        //定义路径结构体 
	int start;              //起点 
	int terminal;           //终点 
	int values;             //权值 
}Path;

int g[9][9];               //路径邻接矩阵 
void newGraph() {
	for(int i = 0; i < 9; i++) {
		for(int j = 0; j < 9; j++) {
			g[i][j] = INT32_MAX;      //最大值意味无路径 
		}
	}
	g[0][1] = 3;           //为邻接矩阵赋值 
	//g[1][0] = 3;
	g[0][5] = 4;
	//g[5][0] = 4;
	g[1][2] = 8;
	//g[2][1] = 8;
	g[1][8] = 5;
	//g[8][1] = 5;
//	g[1][6] = 6;
	g[6][1] = 6;
	//g[2][8] = 2;
	g[8][2] = 2;
//	g[2][3] = 12;
	g[3][2] = 12;
//	g[3][8] = 11;
	g[8][3] = 11;
//	g[3][6] = 14;
	g[6][3] = 14;
//	g[3][7] = 6;
	g[7][3] = 6;
//	g[3][4] = 10;
	g[4][3] = 10;
//	g[4][5] = 18;
	g[5][4] = 18;
//	g[4][7] = 1;
	g[7][4] = 1;
//	g[5][6] = 7;
	g[6][5] = 7;
//	g[6][7] = 9;
	g[7][6] = 9;
	for(int i = 0; i < 9; i++) {                //输出邻接矩阵验证 
			for(int j = 0; j < 9; j++) {
				if(g[i][j] == INT32_MAX) cout << "x ";
				else cout << g[i][j] <<" ";
			}
			cout<<endl;
		}
		cout<<endl;
}

vector<Path> path;                              //存放路径的数组 
queue<Path> queuePath;                          //路径优先队列 
void addPath() {
	for(int i = 0; i < 9; i++) {           
		for(int j = 0; j < 9; j++) {
			Path cur_path;
			if(g[i][j] != INT32_MAX) {          //倘若有路径 
				cur_path.start = i;             //将邻接矩阵中的路径转化为路径结构体,添加到path中 
				cur_path.terminal = j;
				cur_path.values = g[i][j];
				path.push_back(cur_path);
			}
		}
	}
	sort(path.begin(), path.end(), [](const Path& a, const Path& b) {
	    return a.values < b.values; // 根据权值降序排序
	});
	for(Path i : path) {
		//cout << "(" << i.start << "," << i.terminal <<")=" << i.values << endl;
	}
	for(Path i: path) {             //按逆序添加路径到优先队列中 
		queuePath.push(i);
	}
}
vector<int> ans;                   //存放构成生成树的结点 
int path_sum;                      //记录总路径和 
bool closedTree(int i, int j) {
	bool i_exited = false;
	bool j_exited = false;
	
	for(int u : ans) {
		if(u == i) i_exited = true;
		if(u == j) i_exited = true;
	}
	if(i_exited && j_exited) return false;
	else return true;
}

void Kruskal(){
	
	while(!queuePath.empty()) {
		Path choice = queuePath.front();
		if(closedTree(choice.start, choice.terminal)) {
			ans.push_back(choice.start);         //将路径的起点和终点添加到结点数组ans中 
			ans.push_back(choice.terminal);
			path_sum += choice.values;
			cout << "(" <<choice.start << "," << choice.terminal <<")=" << choice.values << endl;
		}
		queuePath.pop();
	} 
    cout << "cost of minimum spanning tree is :" << path_sum;
}

int main() {
	newGraph();
	addPath();
	Kruskal();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一周学会Django5 Python Web开发-Django5 ORM执行SQL语句

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计49条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

Spring 注解开发详解

1. 注解驱动入门案例介绍 1.1 需求描述 1.需求&#xff1a;实现保存一条数据到数据库。 2.表结构&#xff1a;create table account(id int primary key auto_increment,name varchar(50),money double(7,2)); 3.要求&#xff1a;使用spring框架中的JdbcTemplate和DriverMana…

Python 使用相对路径读取文件失败

python open一个问及那时使用绝对路径可以&#xff0c;但是使用相对路径时报错&#xff0c;找不到指定文件 解决步骤如下&#xff1a; 添加Python配置 在新增的配置Json文件添加下图红框这一行

阿里云OSS

进入阿里云官网&#xff0c;手机号短信登录

Ansible 中的copy 复制模块应用详解

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月25日13点40分 Ansible 中的 copy 模块用于将文件或目录从本地计算机或远程主机复制到远程主机上的特定位置。它是一个功能强大的模块&#xff0c;可用于各种文…

prometheus helm install 如何配置告警模版

对接企业微信 获取企业id 注册完成之后&#xff0c;通过企业微信官网登录后台管理&#xff0c;在【我的企业】的企业信息里面&#xff0c;获取到Alertmanager服务配置需用到的第一个配置&#xff1a;企业ID 获取部门id 部门ID 在【通讯录】中&#xff0c;添加一个子部门&a…

无人机+自组网:2U机架车载式自组网电台技术详解

自组网的特点包括自发现、自动配置、自组织和自愈等。由于网络中的节点可以随时加入或离开&#xff0c;自组网需要能够自动感知拓扑结构的变化&#xff0c;并快速调整路由策略以适应新的网络环境。此外&#xff0c;自组网中的节点还需要具备节能、安全和分布式管理等特性&#…

maixcam如何无脑运行运行别人的模型(以安全帽模型为例)

maixcam如何无脑运行运行别人的模型&#xff08;以安全帽模型为例&#xff09; 本文章主要讲如何部署上传的模型文件&#xff0c;以及如果你要把你模型按照该流程应该怎么修改&#xff0c;你可以通过该文章得到你想要的应该&#xff0c;该应用也包含的退出按钮&#xff0c;是屏…

质量管理系统( QMS):一文扫盲,质量重于泰山。

一、什么是QMS系统 QMS系统是质量管理系统&#xff08;Quality Management System&#xff09;的缩写。它是一种组织内部用于管理和控制质量相关活动的体系&#xff0c;旨在确保产品或服务符合质量标准和客户要求。 QMS系统通常包括一系列文件、程序和流程&#xff0c;用于规…

Linux常用命令总结(四):文件权限及相关命令介绍

1. 文件属性信息解读 1. 文件类型和权限的表示 0首位表示类型。在Linux中第一个字符代表这个文件是目录、文件或链接文件 符号对应文件类型-代表文件dd 代表目录l链接文档(link file)&#xff1b; 1-3位确定属主&#xff08;该文件的所有者&#xff09;拥有该文件的权限。 4-6…

【信息收集】端口扫描masscan负载均衡识别lbd

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、什么是masscan masscan在kali系统上是自带的端口扫描…

【golang学习之旅】报错:a declared but not used

目录 报错原因解决方法参考 报错 代码很简单&#xff0c;如下所示。可以发现a和b都飙红了&#xff1a; 运行后就会出现报错&#xff1a; 报错翻译过来就是a已经声明但未使用。当时我很疑惑&#xff0c;在其他语言中从来没有这种情况。况且这里的b不是赋值了吗&#xff0c;怎…

SSL证书一年要多少钱

经常会被人问道&#xff1a;“安装SSL证书一年得要多少钱&#xff1f;”其实证书的类型不同、功能不同、品牌不同&#xff0c;证书的价格也会不一样。SSL证书的价格因其类型、品牌、购买渠道以及所包含的服务和支持等因素而有所不同。通常一年的费用从几十元到几万元不等。 一…

[Swift]单元测试

编写单元测试是确保你的代码质量和功能正确性的重要步骤 一、编写单元测试的详细流程 1. 创建一个新的Xcode项目 如果你尚未创建一个项目&#xff0c;首先你需要在Xcode中创建一个新的iOS项目&#xff1a; 打开Xcode&#xff0c;选择“File” > “New” > “Project”…

【黑马点评Redis——004达人探店】

1.发布探店笔记 2.点赞 利用Redis中的Set集合来判断是否点赞过。 3.点赞排行榜 可以通过SortedSet来按点赞时间进行排序。 4.好友关注 4.1.关注和取关 4.2.共同关注 可以通过set实现交集的功能 4.3.关注推送 4.3.1 拉模式 拉模式&#xff08;Pull&#xff09;&#x…

Android 13 Layer数据结构

Layer::State state的定义 State mDrawingState; 一个mDrawingState的变量struct State {Geometry active_legacy;Geometry requested_legacy;int32_t z;ui::LayerStack layerStack;#endifuint32_t flags;uint8_t reserved[2];int32_t sequence; // changes when visible regi…

解决Oracle锁表的方法

在实际工作中&#xff0c;并发量比较大的项目&#xff0c;经常会出现锁表的问题&#xff0c;下面我将复现这个问题&#xff0c;并给出解决方法。 一、问题复现 1、session1修改aabb表的B字段为迪迦奥特曼&#xff0c;但是不提交该事务。 2、session2也修改这行的这个字段。 发…

基于Springboot的网上商城购物系统

基于SpringbootVue的网上商城购物系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 商品信息 商品资讯 后台登录页面 后台管理首页 用户管理 商品分类管…

SVG 绘制微信订阅号icon

效果 代码 <!DOCTYPE html> <html> <body><svg xmlns"http://www.w3.org/2000/svg" version"1.1" width"600" height"600"><rect x"0" y"0" rx"0" ry"0" width&…

【C++】5.C语言/C++内存管理

一、C/C内存分布 栈中存储的是局部变量&#xff0c;函数参数&#xff0c;返回值等 堆主要用于动态内存分配 数据段用以存储全局数据和静态数据 代码段存储可执行代码和常量 二、C语言和C中的内存管理方式 在C语言中&#xff0c;我们使用 malloc、calloc、realloc、free来进…
最新文章