图论算法基础:最小生成树算法(kruskal算法和Prim算法)

news2024/12/29 8:59:07

在这里插入图片描述

文章目录

  • 一.图邻接矩阵数据结构
  • 二.kruskal算法
    • 算法实现思想
    • kruskal算法接口实现
  • 三.Prim算法
    • Prim算法接口实现

一.图邻接矩阵数据结构

  • 以STLvectorunordered_map为适配容器实现图数据结构:
namespace Graph_Structure
{
	//Vertex是代表顶点的数据类型,Weight是边的权值的数据类型,MAX_W是权值的上限值(表示不相两)
	//Direction表示图是否为有向图
	template<class Vertex, class Weight = int, Weight MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
		typedef Graph<Vertex, Weight, MAX_W, Direction> Self;
	public:
		//使用编译器的默认构造函数
		Graph() = default;

		//给定一个存放顶点的数组用来初始化图
		Graph(const Vertex* a, size_t n)
		{
			_vertexs.reserve(n);
			_indexMap.rehash(n);
			_matrix.resize(n, std::vector<Weight>(n, MAX_W));
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				//建立顶点和数组下标的映射(目的是为了邻接矩阵的边存储)
				_indexMap[a[i]] = i;
			}
		}

		//获取顶点在邻接矩阵中对应的下标
		size_t GetVertexIndex(const Vertex& vertex)
		{
			if (_indexMap.find(vertex) == _indexMap.end())
			{
				throw "invalued_para";
				return -1;
			}
			return _indexMap[vertex];
		}


		void _AddEdge(size_t srci, size_t dsti, const Weight& w)
		{
			//判断是有向图还是无向图
			if (Direction == false)
			{
				_matrix[dsti][srci] = w;
			}
			_matrix[srci][dsti] = w;
		}
		//给定起点和终点,在邻接矩阵中添加一条边
		void AddEdge(const Vertex& src, const Vertex& dst, const Weight& w)
		{
			if (_indexMap.find(src) == _indexMap.end() || _indexMap.find(dst) == _indexMap.end())
			{
				throw "invalued_para";
			}

			size_t srci_index = GetVertexIndex(src);
			size_t dst_index = GetVertexIndex(dst);
			_AddEdge(srci_index, dst_index, w);
		}

		//打印图的接口
		void Print()
		{
			for (auto e : _vertexs)
			{
				std::cout << e << '[' << _indexMap[e] << ']' << std::endl;
			}

			std::cout << "     ";
			for (int i = 0; i < _vertexs.size(); ++i)
			{
				std::cout << i << "    ";
			}
			std::cout << std::endl;


			int i = 0;
			for (auto arry : _matrix)
			{
				std::cout << i++ << ' ';
				for (auto e : arry)
				{
					if (e == MAX_W)
					{
						printf("%4c ", '*');
					}
					else
					{
						printf("%4d ", e);
					}
				}
				std::cout << std::endl;
			}
		}

		//图顶点的广度优先遍历
		void BFS(const Vertex& src)
		{
			size_t begin = GetVertexIndex(src);
			std::queue<int> QNode;
			std::vector<bool> Label(_vertexs.size(), false);
			QNode.push(begin);
			Label[begin] = true;
			size_t Level = 0;
			while (!QNode.empty())
			{
				size_t LevelSize = QNode.size();
				for (size_t i = 0; i < LevelSize; ++i)
				{
					size_t front = QNode.front();
					QNode.pop();
					std::cout << _vertexs[front] << '[' << front << ']' << std::endl;
					for (int j = 0; j < _vertexs.size(); ++j)
					{
						if (Label[j] == false && _matrix[front][j] != MAX_W)
						{
							QNode.push(j);
							Label[j] = true;
						}
					}
				}
			}
		}
		
		//图顶点的深度优先遍历
		void DFS(const Vertex& src)
		{
			std::vector<bool> visited(_vertexs.size(), false);
			_DFS(GetVertexIndex(src), visited);
		}
	private:
		void _DFS(size_t srci, std::vector<bool>& visited)
		{
			visited[srci] = true;
			std::cout << _vertexs[srci] << '[' << srci << ']' << std::endl;
			for (int i = 0; i < _vertexs.size(); ++i)
			{
				if (_matrix[srci][i] != MAX_W && visited[i] == false)
				{
					_DFS(i, visited);
				}
			}
		}

	private:
		std::vector<Vertex> _vertexs;					// 顶点集合
		std::unordered_map<Vertex, size_t> _indexMap;	// 顶点与下标的映射
		std::vector<std::vector<Weight>> _matrix;		// 图的邻接矩阵
	};

}
  • 一个无向连通图的生成树指的是包含该图所有顶点无环连通子图
    • 最小生成树指的是图的所有生成树中边权值之和最小的生成树

二.kruskal算法

  • kruskal算法求图的最小生成树的基本思想是依次选出图中权值最小的边来构建生成树,图中某条边加入生成树之前,要判断生成树中是否会形成环(这是该算法的一个难点),如果会形成环,则跳过这条边继续选下一条权值最小边.若连通图共有N个顶点,则选出N-1条边则最小生成树构建完成

算法实现思想

  • 选最小边的方法:将图的所有边加入到小堆数据结构(STLpriority_queue的底层结构)中,选边时可以从堆顶选到最小边,选N条边的时间复杂度为O(NlogN)
  • 某条边加入生成树之前,判断生成树中是否会出现环的方法:采用并查集实现判环,选边的过程中将生成树中互相连通的顶点在并查集中合并到同一个连通分量中.
    • 最小生成树构建的过程中,如果选出的权值最小边的起点和终点位于并查集的同一个连通分量中,则该边不能加入最小生成树(原理:两个顶点在构建最小生成树的过程中如果两次连通则生成树中必定会出现环)
    • 采用并查集判环,每次判环的时间复杂度接近O(1)
      在这里插入图片描述

kruskal算法接口实现

  • 定义一个描述边的内部类(记录边的起点和终点以及边的权值)
		//定义表示边的结构用于算法设计
		class Edge
		{
		public:
			size_t _srci;//起始点编号
			size_t _dsti;//终点编号
			Weight _weight;//边的权值
			//边的构造函数
			Edge(size_t srci, size_t dsti, Weight weight)
				:_srci(srci),
				_dsti(dsti),
				_weight(weight)
			{}
			//重载比较运算符
			bool operator>(const Edge& edge) const
			{
				return _weight > edge._weight;
			}
		};
  • kruskal算法接口:
		//克鲁斯卡尔算法求最小生成树,返回值是最小生成树的边权值之和
		Weight Kruskal(Self& MinTree)
		{
			//minTree成员的拷贝
			MinTree._vertexs = _vertexs;
			MinTree._indexMap = _indexMap;
			//初始化最小生成树的邻接矩阵
			MinTree._matrix.resize(_vertexs.size(), std::vector<Weight>(_vertexs.size(), MAX_W));


			//建立优先级队列(小堆)
			std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> Qedge;
			//将边存入堆中
			for (int i = 0; i < _matrix.size(); ++i)
			{
				for (int j = 0; j < _matrix.size(); ++j)
				{
					Qedge.push(Edge(i, j, _matrix[i][j]));
				}
			}

			//建立并查集防止在构建最小生成树时形成环
			std::vector<int> FindSet(_vertexs.size(), -1);
			auto FindRoot = [&FindSet](size_t root) {
				while (FindSet[root] >= 0)
				{
					root = FindSet[root];
				}
				return root;
			};


			Weight totalweight = Weight();//记录最小生成树的总权值
			size_t Edgenums = 0;		  //记录生成树中边数
			//开始选堆构建最小生成树
			while (!Qedge.empty() && Edgenums < _vertexs.size() - 1)
			{
				//选出权值最小边
				Edge MinEdge = Qedge.top();
				Qedge.pop();
				//并查集排环
				size_t root1 = FindRoot(MinEdge._srci);
				size_t root2 = FindRoot(MinEdge._dsti);
				if (root1 != root2)
				{
					//在最小生成树中添加边
					MinTree._AddEdge(MinEdge._srci, MinEdge._dsti, MinEdge._weight);
					//打印方便调试
					std::cout << '[' << _vertexs[MinEdge._srci] << ']' << "->" << '[' << _vertexs[MinEdge._dsti] << ']' << std::endl;

					//并查集中等价元素合并
					if (FindSet[root2] > FindSet[root1])
						std::swap(root1, root2);
					FindSet[root2] = root1;

					++Edgenums;
					totalweight += MinEdge._weight;
				}
			}

			//检验最小生成树是否正常生成
			if (Edgenums == _vertexs.size() - 1)
			{
				return totalweight;
			}
			else
			{
				return Weight();
			}
		}
  • 接口测试:
void TestGraphMinTree()
{
	const char str[] = "abcdefghi";
	Graph_Structure::Graph<char, int> g(str, strlen(str));
	g.AddEdge('a', 'b', 4);
	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);

	Graph_Structure::Graph<char, int> kminTree;
	std::cout << "Kruskal:" << g.Kruskal(kminTree) << std::endl;
	kminTree.Print();
}

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

三.Prim算法

  • Prim最小生成树算法思想:用一个顶点集合来记录已经加入最小生成树的顶点,选定一个构建最小生成树的起始顶点(并将该顶点加入顶点集合),将与顶点集合中的所有点相连的边加入堆数据结构中,从堆顶依次选取权值最小边加入最小生成树,然后更新最小生成树顶点集合,如此往复。
    • 若某条边的起点和终点已经在最小生成树顶点集合中,则跳过该边,取下一条权值最小边,通过这种方式可以保证最小生成树中不会出现环
      在这里插入图片描述

Prim算法接口实现

		//给定一个起点,用普利姆算法求最小生成树,返回值是最小生成树的边权值之和
		Weight Prim(Self& MinTree, Vertex start)
		{
			//minTree成员的拷贝
			MinTree._vertexs = _vertexs;
			MinTree._indexMap = _indexMap;
			//初始化最小生成树的邻接矩阵
			MinTree._matrix.resize(_vertexs.size(), std::vector<Weight>(_vertexs.size(), MAX_W));

			//建立优先级队列(小堆)
			std::priority_queue<Edge, std::vector<Edge>, std::greater<Edge>> Qedge;
			size_t starti = GetVertexIndex(start);

			//顶点集合用于记录顶点是否在生成树中
			std::vector<bool> InMinTree(_vertexs.size(), false);
			InMinTree[starti] = true;

			//将与当前点相连的边加入优先级队列
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				if (_matrix[starti][i] != MAX_W)
				{
					Qedge.push(Edge(starti, i, _matrix[starti][i]));
				}
			}

			Weight totalweight = Weight();//记录最小生成树的总权值
			size_t Edgenums = 0;		  //记录生成树中边数
			while (!Qedge.empty() && Edgenums < _vertexs.size() - 1)
			{
				//选出权值最小边
				Edge MinEdge = Qedge.top();
				Qedge.pop();
				//判断边的终点是否在最小生成树中,避免生成环
				if (InMinTree[MinEdge._dsti] == false)
				{
					InMinTree[MinEdge._dsti] = true;
					//在最小生成树中添加边
					MinTree._AddEdge(MinEdge._srci, MinEdge._dsti, MinEdge._weight);
					//打印方便调试
					//std::cout << '[' << _vertexs[MinEdge._srci] << ']' << "->" << '[' << _vertexs[MinEdge._dsti] << ']' << std::endl;
					
					//将与当前点相连的边加入优先级队列
					for (size_t i = 0; i < _vertexs.size(); ++i)
					{
						if (_matrix[MinEdge._dsti][i] != MAX_W && InMinTree[i] == false)
						{
							Qedge.push(Edge(MinEdge._dsti, i, _matrix[MinEdge._dsti][i]));
						}
					}
					++Edgenums;
					totalweight += MinEdge._weight;
				}
			}
			//检验最小生成树是否正常生成
			if (Edgenums == _vertexs.size() - 1)
			{
				return totalweight;
			}
			else
			{
				return Weight();
			}
		}
  • 接口测试:
void TestGraphMinTree()
{
	const char str[] = "abcdefghi";
	Graph_Structure::Graph<char, int> g(str, strlen(str));
	g.AddEdge('a', 'b', 4);
	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);

	Graph_Structure::Graph<char, int> pminTree;
	std::cout << "Prim:" << g.Prim(pminTree, 'a') << std::endl;
	pminTree.Print();
	std::cout << std::endl;
}

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

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

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

相关文章

系统架构设计高级技能 · 大数据架构设计理论与实践

系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估&#xff08;二&#xff09;【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…

最新WAF信息收集技术

WAF信息收集 目前&#xff0c;市面上的WAF大多都部署了云服务进行防护加固&#xff0c;让WAF的防护性能得到进一步提升。 图1-32所示为安全狗最新版服务界面&#xff0c;增加了“加入服云”选项。 安全狗最新版服务界面&#xff0c;不仅加强了传统的WAF防护层&#xff0c;还增…

七层、四层和五层网络模型区别和联系

七层、四层和五层网络模型区别和联系 概述OSI网络7层模型&#xff08;概念型框架&#xff09;概述图片分析 四层模型概述常用协议OSI与TCP/IP四层的区别 五层模型概述三种网络模型对比 总结 概述 网络模型-七层模型&#xff08;OSI模型&#xff09;、五层协议体系结构和TCP/IP…

[C++ 网络协议] 多进程服务器端

具有代表性的并发服务器端实现模型和方法&#xff1a; 多进程服务器&#xff1a;通过创建多个进程提供服务。✔ 多路复用服务器&#xff1a;通过捆绑并统一管理I/O对象提供服务。 多线程服务器&#xff1a;通过生成与客户端等量的线程提供服务。 1. 进程的概念及应用 1.1 什么…

基于算术优化算法优化的BP神经网络(预测应用) - 附代码

基于算术优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于算术优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.算术优化优化BP神经网络2.1 BP神经网络参数设置2.2 算术优化算法应用 4.测试结果&#xff1a;5…

mybatis自动生成文件配置记录

记录下mybatis自动生成mapper文件&#xff0c;虽然现在有点过时了&#xff0c;但对于新手来说还是有一定用处的&#xff08;diss下通过这种文章引流关注的博主&#xff09;。 比较简单&#xff0c;基本就三步搞定&#xff01; 1、pom配置 <!--mybatis自动生成代码插件-->…

【Qt学习】07:绘图与绘图设备

OVERVIEW 绘图与绘图设备一、QPainter二、QPainterDevice1.QPixmap2.QBitmap3.QImage4.QPicture 绘图与绘图设备 一、QPainter Qt 的绘图系统允许使用API在屏幕和其它打印设备上进行绘制&#xff0c;整个绘图系统基于QPainter&#xff0c;QPainterDevice和QPaintEngine三个类&…

Eplan软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Eplan软件是一款专为电气专业设计开发的软件&#xff0c;旨在提高电气设计的效率和质量。以下是Eplan软件的详细介绍。 1、Eplan的历史和演变 Eplan是一款由德国Eplan公司开发的电气设计软件&#xff0c;自1984年推出以来&…

如何在windows电脑上安装多个node,并可以进行随意切换

一、进入官网http://nvm.uihtm.com/ 下载 二、启动解压后的程序 1.开始安装nvm 选择要安装的目录 一直下一步–下一步–最后点击完成 3.最后点击完成即可 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3656568c7e9946e8a04219811fc4c4d3.png 三、在cmd控制台进行操作…

VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小

文章目录 VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小引言创建ESXi7.0可启动 U 盘结果检查VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小 引言 本文讲述了在 J1900平台上安装ESXi7.0时减少 VMFSL 分区占用的说明, 通常这来说些主机内置的磁盘空间非常小, 采用默认安…

uniapp返回上一页并刷新

在uniapp中&#xff0c;经常会有返回上一页的情况&#xff0c;官方提供有 uni.navigateBack 这个api来实现效果&#xff0c;但是此方法返回到上一页之后页面并不会更新&#xff08;刷新&#xff09;。 例如有这样一个场景&#xff1a;从地址列表页点击添加按钮进入添加地址页面…

Aidex 移动端快速开发框架# RuoYi-Uniapp项目,uniapp vue app项目跨域问题

参考地址&#xff1a; manifest.json官方配置文档&#xff1a;manifest.json 应用配置 | uni-app官网 Chrome 调试跨域问题解决方案之插件篇&#xff1a; uni-app H5跨域问题解决方案&#xff08;CORS、Cross-Origin&#xff09; - DCloud问答 其实uni-app官方有解决跨域的办…

「MySQL-01」MySQL基础

目录 一、数据库概念 1. 什么是数据库 2. 为什么要有数据库&#xff1f; 3. 数据库将数据存在哪里&#xff1f; 二、知名数据库介绍 1.知名数据库介绍 2.为什么要学习MySQL 三、MySQL的基本使用 0. 安装MySQL 1. 数据库客户端链接服务端 2. Windows下的MySQL服务端管理 3. 数据…

Pygame编程(10)freetype模块

Pygame编程&#xff08;10&#xff09;freetype模块 函数示例 函数 pygame.freetype.get_error 返回最新的FreeType错误get_error() -> strget_error() -> None pygame.freetype.get_version 返回FreeType版本get_version(linkedTrue) -> (int, int, int) pygame.fre…

斯坦福人生设计课——简略笔记

来源&#xff1a;⽐尔博内特 戴夫伊万斯 著图书《人生设计课》 目录 一、认清当下的情况&#xff0c;从四个维度观察自己的人生 二、平衡人生&#xff0c;但不要走入误区 2.1 记录你的“美好时光日志”&#xff1a; 2.1.1 记录内容&#xff1a; 2.1.2 辅助反思的方法&…

C# 学习笔记--个人学习使用 <2>

C# 学习笔记 Chapter 2 比较硬的基础部分Section 1 委托Part 1 Action 与 func 委托的示例Part 2 自定义委托Part 3 委托的一般使用Part 4 委托的高级使用Part 5 适时地使用接口 Interface 取代一些对委托的使用 Section 2 事件Part 1 初步了解事件Part 2 事件的应用Part 3 事件…

图神经网络与分子表征:番外——基组选择

学过高斯软件的人都知道&#xff0c;我们在撰写输入文件 gjf 时需要准备输入【泛函】和【基组】这两个关键词。 【泛函】敲定计算方法&#xff0c;【基组】则类似格点积分中的密度&#xff0c;与计算精度密切相关。 部分研究人员借用高斯中的一系列基组去包装输入几何信息&am…

快速安装Qt开发环境,克服在线安装慢等问题

自从Qt6之后&#xff0c;QtCreater的安装都需要注册账号&#xff0c;并且使用账号在线安装&#xff0c;继续使用官网的资源站下载的话&#xff0c;会特别的慢&#xff0c;以下是提高在线安装速度的做法。 官网下载很慢&#xff0c;快速安装的方式如下 1、winR,输入CMD&#xff…

深入分析负载均衡情景

本文出现的内核代码来自Linux5.4.28&#xff0c;为了减少篇幅&#xff0c;我们尽量不引用代码&#xff0c;如果有兴趣&#xff0c;读者可以配合代码阅读本文。 一、有几种负载均衡的方式&#xff1f; 整个Linux的负载均衡器有下面的几个类型&#xff1a; 实际上内核的负载均衡…

关于述职答辩的一点思考和总结

公众号&#xff1a;赵侠客 侠客说&#xff1a;优秀人才的四个特征&#xff1a;格局、思路、实干、写作 一、前言 1.1 述职答辩的重要性 公司都会有晋升通道&#xff0c;述职答辩是你想升职加薪除了跳槽以外的必由之路&#xff0c;其重要性对个人发展来说不言而喻&#xff0c…