图【数据结构】

图的基本概念

图是由顶点集合及顶点间的关系组成的一种数据结构
顶点和边:图中结点称为顶点

权值:边附带的数据信息

在这里插入图片描述

在这里插入图片描述
路径 : 在这里插入图片描述

简单路径 和 回路:在这里插入图片描述

子图:设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图
在这里插入图片描述

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

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

生成树就是用最少的边连通起来

最小生成树:构成生成树这些边加起来权值是最小的。

顶点的度:
**加粗样式**
在这里插入图片描述

邻接矩阵

在这里插入图片描述

#pragma once 
#include<map>
#include<vector>
#include<algorithm>
#include<assert.h>
#include<string>
#include <functional>
#include<iostream>
using namespace std;
//矩阵 
namespace matrix
{
	//V是顶点 ,W是 weight 权值 
	template <class V, class W, W MAX_W = INT_MAX, bool Direction = false>  //true是有向图 ,false是无向图
	class Graph
	{
	public:
		//手动添加边
		Graph(const V* a, size_t n)  //用指针数组 存储顶点
		{
			_vertex.reserve(n);
			//初始化顶点和边
			for (size_t i = 0; i < n; i++)
			{
				_vertex.push_back(a[i]);
				_indexMap[a[i]] = i; //通过顶点找下标
			}
			_matrix.resize(n);
			for (size_t i = 0; i < n; i++)
			{
				//将邻接矩阵的权值设置为最大值
				_matrix[i].resize(n, MAX_W);
			}
		}
		size_t  GetVertexIndexMap(const V& v)
		{
			auto  it = _indexMap.find(v);
			if (it != _indexMap.end())
			{
				return it->second;
			}
			else //没有找到 
			{
				assert(false);
				return -1;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			int srci = GetVertexIndexMap(src);
			int dsti = GetVertexIndexMap(dst);

			_matrix[srci][dsti] = w;
			//无向图 
			if (Direction == false)
			{
				_matrix[srci][dsti] = w;
				_matrix[dsti][srci] = w;

			}

		}
			void  Print()
			{

				//顶点 
				for (int i = 0; i < _vertex.size(); i++)
				{
					cout << "[" << i << "]" << "->" << _vertex[i] << endl;
				}
				//矩阵 
				cout << endl;

				//打印横下标
				cout << "  ";
				for (size_t i = 0; i < _vertex.size(); i++)
				{
					cout << i << " ";
				}
				cout << endl;

				for (int i = 0; i < _matrix.size(); i++)
				{
					cout << i << " ";//打印竖下标
					for (int j = 0; j < _matrix[0].size(); j++)

					{
						if (_matrix[i][j] == MAX_W)
						{
							cout << "*"<<" ";
						}
						else
						{
							cout << _matrix[i][j] <<" ";
						}
					}
					cout << endl;
				}
			}
	
public:
	vector<V> _vertex;  //顶点集合 
	map<V, int>  _indexMap;					//顶点映射下标 
	vector< vector<W> >	 _matrix;					//邻接矩阵

};


	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();
	}
}



邻接表

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

出边表:存储从各个顶点连接出去的边,出边表中下标为 i的位置存储的是从编号为i的顶点连接出去的边

入边表:存储连接到各个顶点的边,入边表中下标为i的位置存储的是连接到编号为 i的顶点的边
在这里插入图片描述
出边表和入边表的其中每个位置存储的都是一个链表
出边表中下标为i的位置表示从编号为i的顶点连接出去的边
入边表中下标为i 的位置表示连接到编号为i 的顶点的边

在实现邻接表时,一般只需要用一个出边表来存储从各个顶点连接出去的边即可,因为大多数情况下都是需要从一个顶点出发找与其相连的其他顶点,所以一般不需要存储入边表

//邻接表
namespace link_table
{
	template<class  W>
	struct Edge
	{
		int _dsti;//目标点的下标 
		W _w;//权值
		Edge<W> *_next; //用链表表示边的关系

		Edge(int dsti, const W& w)
			:_dsti(dsti)
			, _w(w)
			, _next(nullptr)  
		{

		}

	};
	//V是顶点 ,W是 weight 权值 
	
	template <class V, class W, bool Direction = false>  //true是有向图 ,false是无向图
	class Graph
	{
	public:
		typedef Edge<W> Edge;
		//手动添加边
		Graph(const V* a, size_t n)  //用指针数组 存储顶点
		{
			_vertex.reserve(n);
			//初始化顶点和边
			for (size_t i = 0; i < n; i++)
			{
				_vertex.push_back(a[i]);
				_indexMap[a[i]] = i; //通过顶点找下标
			}
			_table.resize(n, nullptr);
		
		}
		size_t  GetVertexIndexMap(const V& v)
		{
			auto  it = _indexMap.find(v);
			if (it != _indexMap.end())
			{
				return it->second;
			}
			else //没有找到 
			{
				assert(false);
				return -1;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			int srci = GetVertexIndexMap(src); 
			int dsti = GetVertexIndexMap(dst);

			//头插
			Edge *eg = new Edge(dsti,w);
			//有向图 
			//添加从源顶点到目标顶点的边
			eg->_next = _table[srci];
			_table[srci] = eg;
			

			



			//无向图 

			//添加从目标顶点到源顶点的边
			if (Direction == false)
			{
				//????
				Edge* eg = new Edge(srci, w);
				eg->_next = _table[dsti];
				_table[dsti] = eg;
			}


	

		}
		void  Print()
		{

			//顶点 
			for (int i = 0; i < _vertex.size(); i++)
			{
				cout << "[" << i << "]" << "->" << _vertex[i] << endl;
			}
			cout << endl;
			
			//遍历顶点
			for (size_t i = 0; i < _vertex.size(); i++)
			{
				cout << _vertex[i] << endl;
			}
			//遍历邻接表的目标点的下标和权值 

			for (size_t i = 0; i < _table.size(); i++)
			{
				cout << _vertex[i] << "[" << i << "]->";
				Edge * cur = _table[i];
				while (cur != nullptr)
				{
					cout << "[" << _vertex[cur->_dsti] << ":" << cur->_dsti << ":" << cur->_w << "]->";
					cur = cur->_next;
				}
				cout << "nullptr" << endl;
			}
		}

	public:
		vector<V> _vertex;  //顶点集合 
		map<V, int>  _indexMap;					//顶点映射下标 
		vector<  Edge*>	 _table;					//邻接表

	};


	void TestGraph()
	{
		string a[] = { "张三", "李四", "王五", "赵六" };
		Graph<string, int, true> g1(a, 4);
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 30);
		g1.Print();
	}
}

图的遍历

BFS

在这里插入图片描述


			void BFS(const V& src)  //遍历顶点,通过下标找顶点
			{

				size_t srci = GetVertexIndexMap(src);
				queue<int>  q;

				vector<bool>  v(_vertex.size(), false);  //防止走重复的路

				q.push(srci);

				v[srci] = true;
				while (!q.empty())
				{
						int front = q.front();
						q.pop();
						cout << _vertex[front] << endl;
						// 把front顶点的邻接顶点的下标入队列
						for (size_t i = 0; i < _vertex.size(); i++)
						{
							if (_matrix[front][i] != MAX_W && v[i] == false)
							{
								q.push(i);
								v[i] = true;
							}
						}
					
				}
			}

DFS

在这里插入图片描述


			void _DFS(    size_t srci  ,vector<bool> & v)
			{

				cout << srci << ":" << _vertex[srci] << endl;
				v[srci] = true;

				// 找一个srci相邻的没有访问过的点,去往深度遍历
				for (int i =0 ; i< _vertex.size()  ; i++ )
				{
					if (v[i] ==false && _matrix[srci][i] != MAX_W)
					{
						_DFS(i ,v);
					}
				}

			}
			void  DFS(const V& src) //遍历顶点,通过下标找顶点
			{
				
				vector<bool>  v(_vertex.size(), false);  //防止走重复的路
				size_t  srci = GetVertexIndexMap(src);
				_DFS(srci, v);

			}

完整测试代码

#pragma once 
#include<map>
#include<vector>
#include<algorithm>
#include<assert.h>
#include<string>
#include <functional>
#include<iostream>
#include<queue>
using namespace std;
//矩阵 
namespace matrix
{
	//V是顶点 ,W是 weight 权值 
	//连通的,边的关系就用权值代替,如果两顶点不通,则使用无穷大代替
	template <class V, class W, W MAX_W = INT_MAX, bool Direction = false>  //true是有向图 ,false是无向图
	class Graph
	{
	public:
		//手动添加边
		Graph(const V* a, size_t n)  //用指针数组 存储顶点
		{
			_vertex.reserve(n);
			//初始化顶点和边
			for (size_t i = 0; i < n; i++)
			{
				_vertex.push_back(a[i]);
				_indexMap[a[i]] = i; //通过顶点找下标
			}
			_matrix.resize(n);
			for (size_t i = 0; i < n; i++)
			{
				//将邻接矩阵的权值设置为最大值
				_matrix[i].resize(n, MAX_W);
			}
		}
		size_t  GetVertexIndexMap(const V& v)
		{
			auto  it = _indexMap.find(v);
			if (it != _indexMap.end())
			{
				return it->second;
			}
			else //没有找到 
			{
				assert(false);
				return -1;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			int srci = GetVertexIndexMap(src);
			int dsti = GetVertexIndexMap(dst);

			_matrix[srci][dsti] = w;
			//无向图 
			if (Direction == false)
			{
				_matrix[srci][dsti] = w;
				_matrix[dsti][srci] = w;

			}

		}
			void  Print()
			{

				//顶点 
				for (int i = 0; i < _vertex.size(); i++)
				{
					cout << "[" << i << "]" << "->" << _vertex[i] << endl;
				}
				//矩阵 
				cout << endl;

				//打印横下标
				cout << "  ";
				for (size_t i = 0; i < _vertex.size(); i++)
				{
					cout << i << " ";
				}
				cout << endl;

				for (int i = 0; i < _matrix.size(); i++)
				{
					cout << i << " ";//打印竖下标
					for (int j = 0; j < _matrix[0].size(); j++)

					{
						if (_matrix[i][j] == MAX_W)
						{
							cout << "*"<<" ";
						}
						else
						{
							cout << _matrix[i][j] <<" ";
						}
					}
					cout << endl;
				}
			}


			void BFS(const V& src)  //遍历顶点,通过下标找顶点
			{

				size_t srci = GetVertexIndexMap(src);
				queue<int>  q;

				vector<bool>  v(_vertex.size(), false);  //防止走重复的路

				q.push(srci);

				v[srci] = true;
				while (!q.empty())
				{
						int front = q.front();
						q.pop();
						cout << _vertex[front] << endl;
						// 把front顶点的邻接顶点的下标入队列
						for (size_t i = 0; i < _vertex.size(); i++)
						{
							if (_matrix[front][i] != MAX_W && v[i] == false)
							{
								q.push(i);
								v[i] = true;
							}
						}
					
				}
			}

			void _DFS(    size_t srci  ,vector<bool> & v)
			{

				cout << srci << ":" << _vertex[srci] << endl;
				v[srci] = true;

				// 找一个srci相邻的没有访问过的点,去往深度遍历
				for (int i =0 ; i< _vertex.size()  ; i++ )
				{
					if (v[i] ==false && _matrix[srci][i] != MAX_W)
					{
						_DFS(i ,v);
					}
				}

			}
			void  DFS(const V& src) //遍历顶点,通过下标找顶点
			{
				
				vector<bool>  v(_vertex.size(), false);  //防止走重复的路
				size_t  srci = GetVertexIndexMap(src);
				_DFS(srci, v);

			}
	
public:
	vector<V> _vertex;  //顶点集合 
	map<V, int>  _indexMap;					//顶点映射下标 
	vector< vector<W> >	 _matrix;					//邻接矩阵

};


	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();
	}


	void TestBDFS()
	{
		string a[] = { "张三", "李四", "王五", "赵六", "周七" };
		Graph<string, int> g1(a, sizeof(a) / sizeof(string));
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 30);
		g1.AddEdge("王五", "周七", 30);
		g1.Print();

		//g1.BFS("张三");
		g1.DFS("张三");
	}
}

//邻接表
namespace link_table
{
	template<class  W>
	struct Edge
	{
		int _dsti;//目标点的下标 
		W _w;//权值
		Edge<W> *_next; //用链表表示边的关系

		Edge(int dsti, const W& w)
			:_dsti(dsti)
			, _w(w)
			, _next(nullptr)  
		{

		}

	};





	//V是顶点 ,W是 weight 权值 
	
	template <class V, class W, bool Direction = false>  //true是有向图 ,false是无向图
	class Graph
	{
	public:
		typedef Edge<W> Edge;
		//手动添加边
		Graph(const V* a, size_t n)  //用指针数组 存储顶点
		{
			_vertex.reserve(n);
			//初始化顶点和边
			for (size_t i = 0; i < n; i++)
			{
				_vertex.push_back(a[i]);
				_indexMap[a[i]] = i; //通过顶点找下标
			}
			_table.resize(n, nullptr);
		
		}
		size_t  GetVertexIndexMap(const V& v)
		{
			auto  it = _indexMap.find(v);
			if (it != _indexMap.end())
			{
				return it->second;
			}
			else //没有找到 
			{
				assert(false);
				return -1;
			}
		}

		void AddEdge(const V& src, const V& dst, const W& w)
		{
			int srci = GetVertexIndexMap(src); 
			int dsti = GetVertexIndexMap(dst);

			//头插
			Edge *eg = new Edge(dsti,w);
			//有向图 
			//添加从源顶点到目标顶点的边
			eg->_next = _table[srci];
			_table[srci] = eg;
			

			



			//无向图 

			//添加从目标顶点到源顶点的边
			if (Direction == false)
			{
				//????
				Edge* eg = new Edge(srci, w);
				eg->_next = _table[dsti];
				_table[dsti] = eg;
			}


	

		}
		void  Print()
		{

			//顶点 
			for (int i = 0; i < _vertex.size(); i++)
			{
				cout << "[" << i << "]" << "->" << _vertex[i] << endl;
			}
			cout << endl;
			
			//遍历顶点
			for (size_t i = 0; i < _vertex.size(); i++)
			{
				cout << _vertex[i] << endl;
			}


			//遍历邻接表的目标点的下标和权值 

			for (size_t i = 0; i < _table.size(); i++)
			{
				cout << _vertex[i] << "[" << i << "]->";
				Edge * cur = _table[i];
				while (cur != nullptr)
				{
					cout << "[" << _vertex[cur->_dsti] << ":" << cur->_dsti << ":" << cur->_w << "]->";
					cur = cur->_next;
				}
				cout << "nullptr" << endl;
			}


		}

	public:
		vector<V> _vertex;  //顶点集合 
		map<V, int>  _indexMap;					//顶点映射下标 
		vector<  Edge*>	 _table;					//邻接表

	};


	void TestGraph()
	{
		string a[] = { "张三", "李四", "王五", "赵六" };
		Graph<string, int, true> g1(a, 4);
		g1.AddEdge("张三", "李四", 100);
		g1.AddEdge("张三", "王五", 200);
		g1.AddEdge("王五", "赵六", 30);
		g1.Print();
	}
}
	
  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鄃鳕

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

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

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

打赏作者

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

抵扣说明:

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

余额充值