C++图论基本概念与存储结构

 更新时间:2026年02月23日 11:11:25   作者:落羽的落羽  
这篇文章主要介绍了C++图论基本概念与存储结构,图是由顶点集合及顶点间的关系组成的一种数据结构,一个图既有顶点也有边,图的存储结构中就需要保存它们,下面将进行详细的介绍,需要的朋友可以参考下

一、图的基本概念

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E);

  • V是顶点集合
  • E是顶点间关系的集合,也叫做边的集合
  • 图中的结点称为顶点。如果两个顶点 vi 和 vj 之间相关联,称有一条边记<vi, vj>

图分为有向图和无向图:在有向图中,<x, y>称为顶点x到顶点y的一条边,<x, y>和<y, x>是两条不同的边。而在无向图中,<x, y>和<y, x>是同一条边。

图中边的数量与顶点数量的关系,可以用稠密和稀疏形容。

完全图:设一张图有n个顶点。对于无向图,若有n*(n-1)/2条边,即任意两点之间都有一条边,则称此图为无向完全图,也是最稠密的图;对于有向图,若有n*(n-1)条边,即任意两点之间都有两条方向相反的边,则称此图为有向完全图。

顶点的度:顶点v的度是指与它相关联的边的条数,记作deg(v)。在有向图中,顶点v的度等于该顶点的入度与出度之和,入度是指以v为终点的有向边的条数,出度是指以v为起点的有向边的条数。在无向图中,顶点v的度等于出度等于入度。

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

权值:边附带的数据信息,比如:长度,价值,亲密度

路径长度:对于不带权的图,一条路径的路径长度是该路径上的边数量;对于带权的图,一条路径的路径长度是该路径上的所有边的权总和。

简单路径与回路:若一条路径上的各顶点都不重复,则称这样的路径为简单路径。若路径上的第一个顶点和最后一个顶点重合,则称这样的路径为回路或环。

子图:设图G = {V, E}、G1 = {V1, E1},若V1属于V且E1属于E,则称G1是子图。即一个图的子图,所有的顶点和边都在原图中出现过。

连通图:对于无向图,若顶点v1到v2有路径,则称v1和v2是连通的。如果图中任意两个顶点都是连通的,则称此图为连通图。

强连通图:对于有向图,如果任意两顶点vi和vj之间,都存在一条vi到vj的路径和vj到vi的路径,则称此图为强连通图。

生成树:生成树是连通图(无向图)的一个子图,是一棵树,包含原图所有顶点且保持连通,有n个顶点的连通图的生成树有n个顶点和n-1条边。

二、图的存储结构

一个图既有顶点也有边,图的存储结构中就需要保存它们。顶点保存比较简单,只需要一个数组即可,关系边该怎么保存呢?

保存边的方式,有领接矩阵和领接表两种方式!

1. 领接矩阵

顶点与顶点之间是否连通,可以用0或1表示。领接矩阵就是一个二维数组,用矩阵来表示顶点之间的关系

每个结点可以用数组下标代表。例如,顶点A的下标是x,顶点B的下标是y。领接矩阵中[x][y]代表从A到B的边的权值,如果是无权图就用01表示该边是否存在即可,如果是有权图则填入权值或默认值(表示边不存在,一般可以用INT_MAX代表)。
注意,对于无向图,领接矩阵是左下右上对称的,即[x][y][y][x]内容一样!有向图则不是,[x][y][y][x]内容不一定相同。

// 图
namespace Matrix
{
	// V顶点类型 W边权值类型 MAX_W表示边不存在的值 Direction表示图是否有向
	template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
	public:
		Graph(const V* vertexs, size_t n)
		{
			_vertexs.reserve(n);
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(vertexs[i]);
				_vIndexMap[vertexs[i]] = i;
			}
			// MAX_W 作为不存在边的标识值
			// 初始化时默认没有边,边需要一条一条手动添加,用AddEdge函数
			_matrix.resize(n);
			for (auto& e : _matrix)
			{
				e.resize(n, MAX_W);
			}
		}
		// 找到一个顶点的映射下标
		size_t GetVertexIndex(const V& v)
		{
			auto ret = _vIndexMap.find(v);
			if (ret != _vIndexMap.end())
			{
				return ret->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}
		// 添加一条边,src和dst代表两端顶点,w是权值
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srci = GetVertexIndex(src);
			size_t dsti = GetVertexIndex(dst);
			_matrix[srci][dsti] = w;
			//如果是无向图,则[dsti][srci]也需添加边
			if (Direction == false)
			{
				_matrix[dsti][srci] = w;
			}
		}
	private:
		map<V, size_t> _vIndexMap;   // 顶点到下标的映射
		vector<V> _vertexs;			 // 顶点集合
		vector<vector<W>> _matrix;   // 领接矩阵 存储边
	};
}

领接矩阵适合存储稠密图,能O(1)判断两个顶点的关系,得到权值。但是如果要查找一个顶点连接的所有边,效率是O(n)

2. 领接表

领接表是一个链表数组。数组表示顶点的集合,链表表示边的关系。

领接表适合存储稀疏图,适合查找一个顶点连接出去的边,但是相对不适合判断两个点是否有边及其权值。

// 临接表
namespace LinkTable
{
	// 定义边结构, W是权值类型
	template<class W>
	struct LinkEdge
	{
		int _srcIndex;
		int _dstIndex;
		W _w;
		LinkEdge<W>* _next;
		LinkEdge(const W& w)
			: _srcIndex(-1)
			, _dstIndex(-1)
			, _w(w)
			, _next(nullptr)
		{ }
	};
	template<class V, class W, bool Direction = false>
	class Graph
	{
		typedef LinkEdge<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]);
				_vIndexMap[vertexs[i]] = i;
			}
			_linkTable.resize(n, nullptr);
		}
		size_t GetVertexIndex(const V& v)
		{
			auto ret = _vIndexMap.find(v);
			if (ret != _vIndexMap.end())
			{
				return ret->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}
		// 添加边
		void AddEdge(const V& src, const V& dst, const W& w)
		{
			size_t srcindex = GetVertexIndex(src);
			size_t dstindex = GetVertexIndex(dst);
			Edge* sd_edge = new Edge(w);
			sd_edge->_srcIndex = srcindex;
			sd_edge->_dstIndex = dstindex;
			sd_edge->_next = _linkTable[srcindex];
			_linkTable[srcindex] = sd_edge;
			// 如果是无向图,还要反过来添加一次
			if (Direction == false)
			{
				Edge* ds_edge = new Edge(w);
				ds_edge->_srcIndex = dstindex;
				ds_edge->_dstIndex = srcindex;
				ds_edge->_next = _linkTable[dstindex];
				_linkTable[dstindex] = ds_edge;
			}
		}
	private:
		map<string, int> _vIndexMap; // 顶点到下标的映射
		vector<V> _vertexs;			 // 顶点集合
		vector<Edge*> _linkTable;    // 边的集合的领接表
	};
}

以上就是C++图论基本概念与存储结构的详细内容,更多关于C++图的概念与存储的资料请关注脚本之家其它相关文章!

相关文章

  • C++算法之在无序数组中选择第k小个数的实现方法

    C++算法之在无序数组中选择第k小个数的实现方法

    这篇文章主要介绍了C++算法之在无序数组中选择第k小个数的实现方法,涉及C++数组的遍历、判断、运算等相关操作技巧,需要的朋友可以参考下
    2017-03-03
  • C/C++通过IP获取局域网网卡MAC地址

    C/C++通过IP获取局域网网卡MAC地址

    这篇文章主要为大家详细介绍了C++如何通过Win32API函数SendARP从IP地址获取局域网内网卡的MAC地址,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02
  • C++中utf8字符串和gbk字符串的转换方法

    C++中utf8字符串和gbk字符串的转换方法

    文章介绍了C++中UTF-8字符串和GBK字符串之间的转换,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-02-02
  • OpenCV利用霍夫变换实现交通车道线检测

    OpenCV利用霍夫变换实现交通车道线检测

    经典霍夫变换用来检测图像中的直线,后来霍夫变换经过扩展可以进行任意形状物体的识别,例如圆和椭圆。本文就来利用霍夫变换实现交通车道线检测,需要的可以参考一下
    2022-09-09
  • C语言实现随机发牌

    C语言实现随机发牌

    这篇文章主要为大家详细介绍了C语言实现随机发牌,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • C语言多功能动态通讯录实现示例

    C语言多功能动态通讯录实现示例

    这篇文章主要为大家介绍了C语言多功能动态通讯录实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 单词小助手C语言版

    单词小助手C语言版

    这篇文章主要为大家详细介绍了C语言版的单词小助手,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • 嵌入式QT移植的实现

    嵌入式QT移植的实现

    本文主要介绍了嵌入式QT移植的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 学习C++编程的必备软件

    学习C++编程的必备软件

    本文给大家分享的是作者在学习使用C++进行编程的时候所用到的一些常用的软件,这里推荐给大家
    2017-04-04
  • C语言实现三子棋的步骤和代码详解

    C语言实现三子棋的步骤和代码详解

    这篇文章主要介绍了C语言实现三子棋的步骤和代码详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论