C++实现图—邻接矩阵,邻接表,深度遍历,广度遍历

news2024/11/27 0:21:34

目录

1.图的基本概念

2.图的存储结构

2.1邻接矩阵

2.2 邻接表

3.图的遍历

3.1广度优先遍历

3.2图的深度遍历

 总结:


1.图的基本概念

图是由顶点集合以及顶点之间的关系组成的一种数据结构:G = (V,E),其中顶点集合V={x|x属于某个对象集}是有穷非空集合;

E = {(x,y)|x,y属于V}或者E = {<x, y>|x,y属于V && Path(x, y)}是顶点间关系的有穷集合,也叫
做边的集合。

(x, y)表示x到y的一条双向通路,即(x, y)是无方向的;Path(x, y)表示从x到y的一条单向通路,即
Path(x, y)是有方向的。

顶点和边:图中结点称为顶点,第i个顶点记作vi。两个顶点vi和vj相关联称作顶点vi和顶点vj之间
有一条边,图中的第k条边记作ek,ek = (vi,vj)或<vi,vj>。

有向图和无向图在有向图中,顶点对<x, y>是有序的,顶点对<x,y>称为顶点x到顶点y的一条
边(弧),<x, y>和<y, x>是两条不同的边,
比如下图G3和G4为有向图。在无向图中,顶点对(x, y)
是无序的,顶点对(x,y)称为顶点x和顶点y相关联的一条边,这条边没有特定方向,(x, y)和(y,x)
是同一条边,
比如下图G1和G2为无向图。注意:无向边(x, y)等于有向边<x, y>和<y, x>

完全图:在有n个顶点的无向图中,若有n * (n-1)/2条边,即任意两个顶点之间有且仅有一条边,
则称此图为无向完全图,比如上图G1;在n个顶点的有向图中,若有n * (n-1)条边,即任意两个
顶点之间有且仅有方向相反的边,
则称此图为有向完全图,比如上图G4。 

邻接顶点:在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u,v)依
附于顶点u和v
;在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶
点u,并称边<u, v>与顶点u和顶点v相关联。

顶点的度顶点v的度是指与它相关联的边的条数,记作deg(v)。在有向图中,顶点的度等于该顶
点的入度与出度之和,其中顶点v的入度是以v为终点的有向边的条数
,记作indev(v);顶点v的出度
是以v为起始点的有向边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注
意:对于无向图,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)。

路径:在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶
点序列为从顶点vi到顶点vj的路径。

路径长度:对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一
条路径的路径长度是指该路径上各个边权值的总和。

简单路径与回路若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路
径。若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。
 

子图:设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图。 

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

强连通图在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj
到vi的路径,则称此图是强连通图。

生成树在无向图中,一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。

2.图的存储结构

因为图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和
边关系即可。节点保存比较简单,只需要一段连续空间即可,那边关系该怎么保存呢?

2.1邻接矩阵

因为节点与节点之间的关系就是连通与否,即为0或者1,因此邻接矩阵(二维数组)即是:先用一
个数组将定点保存,然后采用矩阵来表示节点与节点之间的关系

注意:

1. 无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度。有向图的邻接矩阵则不一
定是对称的,第i行(列)元素之后就是顶点i 的出(入)度。
2. 如果边带有权值,并且两个节点之间是连通的,上图中的边的关系就用权值代替,如果两个
顶点不通,则使用无穷大代替。 

3. 用邻接矩阵存储图的优点是能够快速知道两个顶点是否连通,缺陷是如果顶点比较多,边比
较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间,并且要求两个节点之间的路
径不是很好求。 

代码实现:

#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;

namespace matrix
{
	template<class V,class W,W MAX_W = INT_MAX,bool Direction = false>
	class Graph
	{
	public:
		//构造:顶点,边,下标
		//"0123", 4
		Graph(const V* vertexs, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				_vertexs.push_back(vertexs[i]);
				_indexMap[vertexs[i]] = i;
			}
			//MAX_W作为不存在边的标识
			_matrix.resize(n);
			for (auto& e : _matrix)
			{
				e.resize(n, MAX_W);
			}
		}
		void _addEdge(size_t srci, size_t dsti, const W& w)
		{
			_matrix[srci][dsti] = w;
			if (Direction == false)
				_matrix[dsti][srci] = w;
		}
		size_t GetVertexIndex(const V& v)
		{
			auto ret = _indexMap.find(v);
			if (ret != _indexMap.end())
				return ret->second;
			else
				return -1;
		}
		//添加边:
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			_addEdge(srci, dsti, w);
		}
		void Print()
		{
			//打印顶点和下标的映射关系
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				cout << _vertexs[i] << "-" << i << " ";
			}
			cout << endl;
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				if (i == 0)
					cout << "  ";
				cout << i << " ";
			}
			cout << endl;
			//打印矩阵:
			for (size_t i = 0; i < _matrix.size(); i++)
			{
				cout << i << " ";
				for (size_t j = 0; j < _matrix[i].size(); j++)
				{
					if (_matrix[i][j] != INT_MAX)
						cout << _matrix[i][j] << " ";
					else
						cout << "*" << " ";
				}
				cout << endl;
			}
			cout << endl;
			//打印所有的边:
			for (size_t i = 0; i < _matrix.size(); i++)
			{
				for (size_t j = 0; j < _matrix[i].size(); j++)
				{
					if (_matrix[i][j] != INT_MAX)
						cout << _vertexs[i] << "-" << _vertexs[j] << ":" << _matrix[i][j] << endl;
				}
			}
		}
	private:
		vector<V> _vertexs; //存储顶点的集合
		vector<vector<W>> _matrix; //存储边集合的矩阵
		map<V, int> _indexMap;//顶点映射下标
	};
	void TestGraph()
	{
		Graph<char, int, INT_MAX, true> g("0123", 4);
		g.AddEdge('0', '1', 1);
		g.AddEdge('0', '3', 4);
		g.AddEdge('1', '3', 2);
		g.AddEdge('1', '2', 9);
		g.AddEdge('2', '3', 8);
		g.AddEdge('2', '1', 5);
		g.AddEdge('2', '0', 3);
		g.AddEdge('3', '2', 6);

		g.Print();
	}
}

2.2 邻接表

邻接表:使用数组表示顶点的集合,使用链表表示边的关系。

1. 无向图邻接表存储

注意:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi边链表集合中结点的数目即可。

 

注意:有向图中每条边在邻接表中只出现一次,与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表,要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i。 

代码实现:

#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;
//邻接表
namespace link_table
{
	template<class W>
	struct Edge
	{
		int _destIndex;
		W _w;
		Edge<W>* _next;
		Edge(int destIndex,const W&w)
			:_destIndex(destIndex),_w(w),_next(nullptr) {}
	};
	template<class V, class W, bool Direction = false>
	class Graph
	{
		typedef Edge<W> Edge;
	public:
		Graph(const V* vertexs, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				_vertexs.push_back(vertexs[i]);
				_indexMap[vertexs[i]] = i;
			}
			_tables.resize(n, nullptr);
		}
		size_t GetVertexIndex(const V& v)
		{
			auto ret = _indexMap.find(v);
			if (ret != _indexMap.end())
				return ret->second;
			else
				return -1;
		}
		//添加边:
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			Edge* eg = new Edge(dsti, w);
			eg->_next = _tables[srci];
			_tables[srci] = eg;
			if (Direction == false)
			{
				Edge* eg = new Edge(srci, w);
				eg->_next = _tables[dsti];
				_tables[dsti] = eg;
			}
		}
		void Print()
		{
			//打印顶点和下标的映射关系
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				cout << _vertexs[i] << "-" << i << " ";
			}
			//打印连接的边:
			for (size_t i = 0; i < _tables.size(); i++)
			{
				cout << _vertexs[i] << "[" << i << "]->";
				Edge* cur = _tables[i];
				while (cur)
				{
					cout <<"[" << _vertexs[cur->_destIndex] << "[" << cur->_destIndex << "]" << cur->_w << "]->";
					cur = cur->_next;
				}
				cout << endl;
			}
		}
	private:
		vector<V> _vertexs; //存储顶点的集合
		map<V, int> _indexMap;//顶点映射下标
		vector<Edge*> _tables;
	};
	void TestGraph()
	{
		string a[] = { "张三", "李四", "王五", "赵六" };
		Graph<string, int> g1(a, 4);
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 30);
		g1.Print();
	}
}

3.图的遍历

3.1广度优先遍历

给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶
点仅被遍历一次。

 代码实现:

        void BFS(const V& src)
		{
			size_t srci = GetVertexIndex(src);
			queue<int> q;
			//标记访问过的顶点
			vector<bool> visited(_vertexs.size(), false);
			q.push(srci);
			int levelSize = q.size();
			visited[srci] = true;
			while (!q.empty())
			{
				//一层一层的出
				for (int i = 0; i < levelSize; i++)
				{
					int front = q.front();
					q.pop();
					cout << front << ":" << _vertexs[front] << " ";
					for (size_t i = 0; i < _vertexs.size(); i++)
					{
						if (_matrix[front][i] != INT_MAX)
						{
							if (visited[i] == false)
							{
								q.push(i);
								visited[i] = true;
							}
						}
					}
				}
				cout << endl;
				levelSize = q.size();
			}
			cout << endl;
		}

3.2图的深度遍历

 

 

        void _DFS(size_t srci, vector<bool>& visited)
		{
			cout << srci << ":" << _vertexs[srci] << endl;
			visited[srci] = true;
			//找相邻的点,深度进行遍历
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				if(_matrix[srci][i] != INT_MAX && visited[i] == false)
					_DFS(i, visited);
			}
		}
		void DFS(const V& src)
		{
			size_t srci = GetVertexIndex(src);
			vector<bool> visited(_vertexs.size(), false);
			_DFS(srci, visited);
		}

 总结:

本篇文章为大家介绍了什么是图,图的两种实现方式邻接矩阵和邻接表,以及图的两种遍历方式包含深度遍历和广度遍历,相信看完之后对图会有一个新的认识,接下来将继续为大家更新图的相关内容,感谢大家的支持!!!

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

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

相关文章

“西湖论剑”四大观察:十年筑梦向未来,数字安全开新局

既有人工智能与安全何去何从的激烈讨论&#xff0c;又有数据安全与数据治理的深度解读&#xff0c;还有数字中国建设背景下安全产业升级的蓝图规划&#xff0c;更有数字安全人才培养的期许与行动……这就是2023 西湖论剑数字安全大会所呈现出的一片热闹景象。 自2012年&#x…

2023全网最全真人总结的常见接口测试面试题,一定不能错过

1、按你的理解&#xff0c;软件接口是什么&#xff1f; 答&#xff1a; 就是指程序中具体负责在不同模块之间传输或接受数据的并做处理的类或者函数。 2、HTTP和HTTPS协议区别&#xff1f; 答&#xff1a; https协议需要到CA&#xff08;Certificate Authority&#xff0c;证书…

理论力学专题:理论力学(物理类)框架

理论力学专题&#xff1a;理论力学&#xff08;物理类&#xff09;框架 拉格朗日方程 虚位移&#xff1a;任意方向的微变化 约束分类&#xff1a; 稳定/不稳定&#xff08;显含时间与否&#xff09;可解/不可解&#xff1a;完整/微分约束&#xff1a; 几何约束&#xff08;完…

F5—创建DDR3内存条DIMM读写测试程序2023-05-16

本文区别于DDR颗粒的配置&#xff0c;记录几个与颗粒配置不同的地方&#xff0c;具体DDR的原理请查看DDR3的应用总结&#xff08;一&#xff09;DDR3的应用总结&#xff08;二&#xff09; 1.确认板卡FPGA型号为xc7k325tffg900 -2&#xff0c;据此创建FPGA工程。 2.添加MIG I…

干货|SPSS方差分析中的简单效应检验(上)

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 今天和大家分享的是包寒吴霜博士生介绍的 SPSS 方差分析中的简单效应检验系列中的第一部分 —— SPSS 方差分析中的简单效应检验&#xff1a;完整教程。 SPSS 是一个入门级的统计分析软件&…

【论文笔记】数据增强系列.1

本文介绍简单数据增强、好处以及常见的增强方式&#xff0c;也介绍几篇关于数据增强的工作&#xff1a; CutMix&#xff08;ICCV2019&#xff09;&#xff0c;ContrastMask&#xff08;CVPR2022&#xff09;&#xff0c;BCP&#xff08;CVPR2023&#xff09;。 数据增强简介&a…

Nginx缓存优雅清除缓存

1.Nginx缓存 前面我们知道Nginx可以对浏览器缓存进行配置&#xff0c;让一些静态资源缓存到用户本地存储&#xff0c;以提高页面的响应速度&#xff0c;也能降低服务端的压力。浏览器执行缓存的流程如下&#xff1a; 试想一下&#xff0c;如果用户主动清空了本地的浏览器缓存&…

HNU-操作系统-讨论课6

讨论题目&#xff1a; 以一种程序设计语言为例&#xff0c;如 Java、C、Python等介绍其为实现并发控制提供的各种锁机制

doxygen: 在Windows上源码编译(施工中)

文章目录 1. 目的2. 思路3. 安装 Chocolatey4. 用 choco 安装 bison 和 flex安装 gs:安装 libiconv 5. 编译报错 1. 目的 在 windows 上源码编译 doxygen&#xff0c; 改代码加功能。 2. 思路 doxygen 依赖 flex 和 bison&#xff0c; 手动编译 flex 和 bison 很麻烦可以用…

【华为OD机试c++】九宫格游戏【2023 Q1 A卷|200分】

■ 题目描述 九宫格是一款广为流传的游戏,起源于河图洛书。 游戏规则是:1到9九个数字放在33的格子中,要求每行、 每列以及两个对角线上的三数之和都等于15. 在金麻名著《射雕英雄传》中黃蓉曾给九宫格的一种解法,口诀: 戴九恩一,左三右七,二四有肩,八六为足,五居…

通过命令行体验长安链

通过命令行体验长安链 1 、概述2、环境依赖2.1、硬件依赖2.2、软件依赖2.3、git安装2.4、golang安装2.5、gcc 3、环境搭建3.1、源码下载3.2、 源码编译3.3、编译及安装包制作3.4、启动节点集群3.5、查看节点启动使用正常 官方文档 https://docs.chainmaker.org.cn/v2.3.1/html/…

亿信BI专有名词讲解

数据库连接池主题域/主题集/主题表维/ 维表报表模板组件容器布局计算参数分析区浮动门户EasyOlap领导驾驶舱(Dashboard) 1.数据库连接池 连接池就是存储资源和数据的地方。BI一定会有一个缺省连接池&#xff0c;BI服务器的系统表都是在缺省连接池下面&#xff0c;初次部署BI服…

30.SSM框架整合

目录 一、SSM框架整合。 &#xff08;1&#xff09;核心笔记。 &#xff08;1.1&#xff09;Spring、SpringMVC、MyBatis三者的配置。 &#xff08;1.2&#xff09;请求字符集格式与响应字符集格式。 &#xff08;2&#xff09;原始方式整合。 &#xff08;2.1&#xff…

路径规划算法:基于郊狼算法的路径规划算法- 附代码

路径规划算法&#xff1a;基于郊狼优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于郊狼优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法郊狼…

带你实现初阶扫雷小游戏—【C语言】

目录 1. 扫雷游戏实现的思路 注意点1 注意点2 2. 函数实现扫雷功能 2.1 初始化棋盘 2.2 显示棋盘 2.3 设置雷 2.4 排查雷 2.5 返回附近雷的个数 3.源码 3.1 game.h 3.2 game.c 3.3 test.c 1. 扫雷游戏实现的思路 注意点1 我们这里拿9*9的棋盘&#xff08;其中…

Java的基操,基操(二)

&#x1f525;常量(Constant)&#x1f525;基本数据类型(primitive data type)&#x1f525;整型&#x1f525;浮点型(Floating Point Number)&#x1f525;字符型&#x1f525;布尔型(boolean)&#x1f525;运算符(operator)&#x1f525;逻辑运算符&#x1f525;数据类型的转…

【C++初阶】:动态管理

动态管理 一.new和delete&#xff08;一般使用&#xff09;二.operator new与operator delete函数&#xff08;底层&#xff09;三.new和delete的实现原理四.定位new&#xff08;placement-new&#xff09;五.malloc和new的区别 前置知识&#xff08;堆区&#xff09;&#xff…

什么样的项目适合UI自动化测试?

我们在考虑做自动化测试之前&#xff0c;一定要先分析一下&#xff0c;这个项目到底适不适合做自动化测试&#xff0c;避免在不太适合自动化测试的项目中痛苦挣扎&#xff0c;既浪费了大量的人力和时间&#xff0c;又收效甚微。下面简单列举一下评估一下项目是否适合做自动化的…

【JavaEE初阶】网络编程

文章目录 网络编程基础网络资源网络编程网络编程中的基本概念发送端和接收端请求和响应客户端和服务端常见的客户端服务端模型 Socket套接字了解UDP和TCPUDP数据报套接字编程DatagramSocket APIDatagramPacket APIInetSocketAddress APIUDP版本的客户端服务器程序 TCP流套接字编…

try catch finally 里面有return的执行顺序

目录 实例结论 实例 1.try和catch中有return时&#xff0c;finally里面的语句会被执行吗 我们可以来分别看看 (1)执行try中的return时 public class Solution {public static int show() {try {return 1;}finally{System.out.println("finally模块被执行");}}publi…