C++ 图(邻接矩阵和邻接表结构)


一、图的基本概念

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

在这里插入图片描述

在这里插入图片描述


二、图的存储结构

因为图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和边关系即可。节点保存比较简单,只需要一段连续空间即可。
其边关系一般采用邻接矩阵邻接表的方式保存。

2.1. 邻接矩阵

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

在这里插入图片描述

注意:

  1. 无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度。有向图的邻接矩阵则不一定是对称的,第i行(列)元素之后就是顶点i 的出(入)度。
  2. 如果边带有权值,并且两个节点之间是连通的,上图中的边的关系就用权值代替,如果两个顶点不通,则使用无穷大代替。
    在这里插入图片描述
  3. 邻接矩阵的优势是快速找到两个顶点之间的边,适合于边比较多的图;其劣势为要找一个顶点连出去的边,需要遍历其他顶点,因此时间复杂度为O(N),并且如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间。

2.2. 邻接表

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

  1. 无向图邻接表存储
    在这里插入图片描述

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

  1. 有向图邻接表存储

在这里插入图片描述

邻接表的优势为:

  1. 找一个点相连的顶点的边很快。
  2. 适合边比较少,比较稀疏的图。
    劣势为:
    要确认两个顶点是否相连为O(N),因为要把所有的边都找一遍。

三、图的广度优先遍历(BFS)

在这里插入图片描述


四、图的深度优先遍历(DFS)

在这里插入图片描述


五、代码实现(邻接矩阵和邻接表)

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

//邻接矩阵
namespace Matrix
{
	//Direction表示是否为有向图,V表示中节点的属性,W表示边的权值
	template<class V, class W,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;
			}

			_matrix.resize(n);
			for (auto& e : _matrix)
			{
				e.resize(n,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;
			}
		}
		void AddEdge(const V& src, const V& dst,const W&w)
		{
			size_t srcindex = GetVertexIndex(src);
			size_t dstindex = GetVertexIndex(dst);
			_matrix[srcindex][dstindex] = w;
			if (Direction == false)
			{
				//无向图
				_matrix[dstindex][srcindex] = w;

			}
		}
		//广度优先搜索
		void BFS(const V& src)
		{
			//找到其下标
			size_t srcindex = GetVertexIndex(src);
			//visited表示没有被访问过的顶点
			vector<bool>visited;
			visited.resize(_vertexs.size(), false);
			//使用队列完成广度遍历
			queue<int>q;
			q.push(srcindex);
			visited[srcindex] = true;
			size_t d = 1;

			//dSize保证队列中的数据走完
			size_t dSize = 1;
			while (!q.empty())
			{
				printf("%s的%d度好友:", src.c_str(), d);
				while (dSize--)
				{
					size_t front = q.front();
					q.pop();
					for (size_t i = 0; i < _vertexs.size(); ++i)
					{
						if (visited[i] == false && _matrix[front][i] != W())
						{
							printf("[%d:%s]", i, _vertexs[i].c_str());
							q.push(i);
							visited[i] = true;
						}
					}
				}
				cout << endl;
				dSize = q.size();
				++d;
			}

		}
		//深度优先搜索
		void _DFS(size_t srcIndex, vector<bool>& visited)
		{
			printf("[%d:%s]", srcIndex, _vertexs[srcIndex].c_str());
			visited[srcIndex] = true;
			for (size_t i = 0; i < _vertexs.size(); ++i)
			{
				if (visited[i] == false&&_matrix[srcIndex][i]!=W())
				{
					_DFS(i, visited);
				}
			}

		}
		void DFS(const V& src)
		{
			//找到其下标
			size_t srcindex = GetVertexIndex(src);
			//visited表示没有被访问过的顶点
			vector<bool>visited;
			visited.resize(_vertexs.size(), false);
			_DFS(srcindex, visited);
		}
	private:
		map<V, int> _VIndexMap;//顶点和下标的映射关系
		vector<V> _vertexs;//顶点集合
		vector<vector<W>> _matrix;//存储边集合的矩阵
	};

	void TestGraph()
	{
		string a[] = { "张三","李四","王五","赵六","周七"};
		Graph<string, int>g1(a,sizeof(a)/sizeof(a[0]));
		g1.AddEdge("张三","李四",100);
		g1.AddEdge("张三","王五",200);
		g1.AddEdge("王五","赵六",10);
		g1.AddEdge("李四","周七",50);
		g1.BFS("张三");
		g1.DFS("张三");
	}

}



//邻接表
namespace LinkTable
{
	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)
		{}
	};
	//Direction表示是否为有向图,V表示中节点的属性,W表示边的权值
	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;
			}
		}

		void BFS(const V& src) 
		{
			//找到其下标
			size_t srcindex = GetVertexIndex(src);
			//visited表示没有被访问过的顶点
			vector<bool>visited;
			visited.resize(_vertexs.size(), false);
			//使用队列完成广度遍历
			queue<int>q;
			q.push(srcindex);
			//visited[srcindex] = true;
			size_t d = 0;
			//dSize保证队列中的数据走完
			size_t dSize = 1;
			while (!q.empty())
			{
				printf("%s的%d度好友:", src.c_str(), d);
				while (dSize--)
				{
					size_t index = q.front();
					q.pop();
					if (!visited[index])
					{
						cout << _vertexs[index] << " ";
						visited[index] = true;
						Edge* pCur = _linkTable[index];
						while (pCur)
						{
							q.push(pCur->_dstIndex);
							pCur = pCur->_next;
						}
					}
				}

				cout << endl;
				dSize = q.size();
				++d;
			}
		}
		void _DFS(int index, vector<bool>& visited) {
			if (!visited[index])
			{
				cout << _vertexs[index] << " ";
				visited[index] = true;
				Edge* pCur = _linkTable[index];
				while (pCur)
				{
					_DFS(pCur->_dstIndex, visited);
					pCur = pCur->_next;
				}
			}
		}
		void DFS(const V& v) {
			vector<bool> visited(_vertexs.size(), false);
			_DFS(GetVertexIndex(v), visited);
			for (size_t index = 0; index < _vertexs.size(); ++index)
			{
				_DFS(index, visited);

			}
			cout << endl;
		}
		//非递归深度优先遍历
		void DFS_non_recursive(const V& v) {
			size_t index = GetVertexIndex(v);
			stack<size_t>st;
			set<size_t>set;
			st.push(index);
			set.insert(index);
			cout << _vertexs[index] << " ";
			while (!st.empty())
			{
				Edge* cur = _linkTable[st.top()];
				st.pop();
				while (cur)
				{
					if (!set.count(cur->_dstIndex))
					{
						st.push(cur->_srcIndex);
						st.push(cur->_dstIndex);
						set.insert(cur->_dstIndex);
						cout << _vertexs[cur->_dstIndex] << " ";
						cur = cur->_next;
						break;
					}
					cur = cur->_next;
				}
			}
		}
		
	private:
		map<V, int> _VIndexMap;//顶点和下标的映射关系
		vector<V> _vertexs;//顶点集合
		vector<Edge*> _linkTable;//边的集合的邻接表
	};

	void TestGraph()
	{
		string a[] = { "张三","李四","王五","赵六","周七" };
		Graph<string, int>g1(a, sizeof(a) / sizeof(a[0]));
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 10);
		g1.AddEdge("李四", "周七", 50);
		g1.BFS("张三");
		g1.DFS("张三");

	}

}
  • 6
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值