C++ implementation graph - adjacency matrix, adjacency list, depth traversal, breadth traversal

Table of contents

1. Basic concept of graph

2. The storage structure of the graph

2.1 Adjacency matrix

2.2 Adjacency list

3. Graph traversal

3.1 Breadth-first traversal

3.2 Depth Traversal of Graphs

 Summarize:


1. Basic concept of graph

A graph is a data structure composed of a set of vertices and the relationship between vertices: G = (V, E), where the set of vertices V={x|x belongs to an object set} is a finite non-empty set;

E = {(x,y)|x,y belongs to V} or E = {<x, y>|x,y belongs to V && Path(x, y)} is a finite set of relationships between vertices, also
called A collection of edges.

(x, y) represents a two-way path from x to y, that is, (x, y) is directionless; Path(x, y) represents a one-way path from x to y, that is, Path(x, y)
is Directed.

Vertices and edges: The nodes in the graph are called vertices, and the i-th vertex is denoted as vi. Two vertices vi and vj are associated and
there is an edge between vertex vi and vertex vj, and the kth edge in the graph is denoted as ek, ek = (vi, vj) or <vi, vj>.

Directed and undirected graphs : In a directed graph, the vertex pair <x, y> is ordered, and the vertex pair <x, y> is called an edge (arc) from vertex x to vertex y. <x
, y> and <y, x> are two different edges.
For example, G3 and G4 in the figure below are directed graphs. In an undirected graph, the vertex pair (x, y)
is unordered, and the vertex pair (x, y) is called an edge associated with vertex x and vertex y. This edge has no specific direction, (x, y) and (y, x)
are the same edge,
for example, the graphs G1 and G2 below are undirected graphs. Note: The undirected edge (x, y) is equal to the directed edges <x, y> and <y, x> .

Complete graph: In an undirected graph with n vertices, if there are n * (n-1)/2 edges, that is, there is only one edge between any two vertices, then the graph is called undirected and
complete Graph, such as the above graph G1; in a directed graph with n vertices, if there are n * (n-1) edges, that is,
there are only edges in opposite directions between any two vertices,
then the graph is called A directed complete graph, such as G4 above. 

Adjacent vertices : In the undirected graph G, if (u, v) is an edge in E(G), then u and v are said to be adjacent vertices to each other, and the edge (u, v) is said
to be attached to vertex u and v
; in the directed graph G, if <u, v> is an edge in E(G), then the vertex u is said to be adjacent to v, and the vertex v is adjacent to the
vertex edge is called <u, v> Associated with vertex u and vertex v.

Vertex degree : The degree of a vertex v refers to the number of edges associated with it , denoted as deg(v). In a directed graph, the degree of a vertex is equal to
the sum of the in-degree and out-degree of the vertex, where the in-degree of a vertex v is the number of directed edges whose end point is v
, denoted as indev(v); vertex v The out-degree of
is the number of directed edges starting from v, denoted as outdev(v). Therefore: dev(v) = indev(v) + outdev(v). Note
: For an undirected graph, the degree of a vertex is equal to the in-degree and out-degree of the vertex , ie dev(v) = indev(v) = outdev(v).

Path : In graph G = (V, E), if there is a group of edges starting from vertex vi to reach vertex vj, then the vertex sequence from vertex vi to vertex vj is called the path from vertex vi to vertex vj
.

Path length : For an unweighted graph, the path length of a path refers to the number of edges on the path; for a weighted graph, the path length of a path refers to the sum of the weights of
each edge on the path.

Simple path and circuit : If the vertices v1, v2, v3, ..., vm on the path are not repeated, then such a path is called a simple path
. If the first vertex v1 on the path coincides with the last vertex vm, then such a path is called a loop or a ring.
 

Subgraph : Let graph G = {V, E} and graph G1 = {V1, E1}, if V1 belongs to V and E1 belongs to E, then G1 is said to be a subgraph of G. 

Connected graph: In an undirected graph, if there is a path from vertex v1 to vertex v2, then vertex v1 and vertex v2 are said to be connected. If
any pair of vertices in a graph is connected
, the graph is called a connected graph. 

Strongly Connected Graph : In a directed graph, if there is a path from vi to vj and a path from vj to vi between every pair of vertices vi and vj, then the graph is said to be
strongly connected.

Spanning tree : In an undirected graph, the smallest connected subgraph of a connected graph is called the spanning tree of the graph . The spanning tree of a connected graph with n vertices has n vertices and n-1 edges.

2. The storage structure of the graph

Because there are both nodes and edges in the graph (the relationship between nodes), therefore, in the storage of the graph, only the
relationship between nodes and edges needs to be saved. Node saving is relatively simple, only a continuous space is needed, how to save the relationship over there?

2.1 Adjacency matrix

Because the relationship between nodes is connected or not, that is, 0 or 1, so the adjacency matrix (two-dimensional array) is: first use an array to save the fixed point, and then use the matrix
to represent the connection between nodes relationship .

Notice:

1. The adjacency matrix of an undirected graph is symmetric, and the sum of elements in the i-th row (column) is the degree of the vertex i. The adjacency matrix of a directed graph
is not necessarily symmetrical, and after the i-th row (column) element is the out (in) degree of vertex i.
2. If the edge has a weight and the two nodes are connected, the edge relationship in the above figure is replaced by the weight. If the two
vertices are not connected, the infinity is used instead. 

3. The advantage of using an adjacency matrix to store a graph is that it can quickly know whether two vertices are connected. The disadvantage is that if there are many vertices and
few 0s are stored in the matrix to become a coefficient matrix, which is a waste of space and requires two The path between nodes
is not very easy to find. 

Code:

#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 Adjacency list

Adjacency list: Use an array to represent the collection of vertices, and use a linked list to represent the relationship of edges.

1. Undirected graph adjacency list storage

Note: The same edge in the undirected graph appears twice in the adjacency list. If you want to know the degree of vertex vi, you only need to know the number of nodes in the edge list set of vertex vi.

 

Note: Each edge in the directed graph only appears once in the adjacency list. The number of nodes contained in the adjacency list corresponding to the vertex vi is the out-degree of the vertex, also known as the out-degree table. To get the vertex of vi In-degree, the edge list corresponding to all other vertices must be checked to see how many edges and vertices have the dst value of i. 

Code:

#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. Graph traversal

3.1 Breadth-first traversal

Given a graph G and any vertex v0 in it, starting from v0, visit all vertices in the graph along each edge in the graph , and each vertex
is traversed only once.

 Code:

        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 Depth Traversal of Graphs

 

 

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

 Summarize:

This article introduces what a graph is, the two implementations of graphs, adjacency matrix and adjacency list, and the two traversal methods of graphs, including depth traversal and breadth traversal. I believe that after reading it, you will have a new understanding of graphs. In the future, we will continue to update the relevant content of the picture for everyone, thank you for your support! ! !

Guess you like

Origin blog.csdn.net/qq_65307907/article/details/130660804