Minimum Spanning Tree—Kruskal Algorithm and Prim Algorithm

1. Minimum spanning tree

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.

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

Minimum spanning tree: the edge that makes up the spanning tree adds up to the smallest weight

Each spanning tree in the connected graph is a very large acyclic subgraph of the original graph, that is, if any edge is deleted from it, the spanning tree
will no longer be connected; on the contrary, any new edge introduced in it will be form a loop.

If a connected graph consists of n vertices, its spanning tree must contain n vertices and n-1 edges. Therefore, there are three criteria for constructing a minimum spanning tree
:
        1. Only the edge with the smallest weight in the graph can be used to construct a minimum spanning tree.
        2. Only exactly n-1 edges can be used to connect n vertices in the graph
        . 3. The selected n-1 edges cannot form a loop.

The method of constructing the minimum spanning tree: Kruskal algorithm and Prim algorithm. Both algorithms adopt a greedy strategy of step-by-step solution.
Greedy algorithm: It means that when solving a problem, always make the choice that looks best at the moment. That is to say, what the greedy algorithm makes is not
the overall
optimal choice, but a local optimal solution in a certain sense. The greedy algorithm cannot obtain the overall optimal solution for all problems.

2. Kruskal algorithm

Given any connected network N={V,E} with n vertices, first construct a graph G={V,NULL} composed of these n vertices without any edges, where each vertex forms a connected Second, continuously take out an edge with the smallest weight from E (if there are multiple ones), if the two vertices of the edge come from different connected components, then add this edge to G. Repeat this until all vertices are on the same connected component.
Core: In each iteration, select an edge with the minimum weight and whose two ends are not on the same connected component, and add it to the spanning tree.

Implementation idea: use the priority queue to store each weight value in ascending order, take out the values ​​in the priority queue in turn, and judge whether the current edge as the edge of the minimum spanning tree forms a loop. If it forms a loop, use And search for exclusion.

3. Prim algorithm

Implementation idea: Give a source point, with the help of two vector arrays X and Y, add the source point from X, then select the edge with the smallest weight in Y and add it to the priority queue, and then remove the values ​​in the priority queue in turn , if the target point of the smallest side is also in the X set, it means that the ring is formed, otherwise the side is the smallest side, add the side, then update the X array and Y array and continue to select the side

4. Code implementation

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

namespace matrix
{
	class UnionFindSet
	{
	public:
		UnionFindSet(size_t size)
			: _ufs(size, -1) {}
		int FindRoot(size_t index)
		{
			while (_ufs[index] >= 0)
			{
				index = _ufs[index];
			}
			return index;
		}
		bool Union(int x1, int x2)
		{
			int root1 = FindRoot(x1);
			int root2 = FindRoot(x2);
			if (root1 == root2)
				return false;
			_ufs[root1] += _ufs[root2];
			_ufs[root2] = root1;
			return true;
		}
		bool InSet(int x1, int x2)
		{
			return FindRoot(x1) == FindRoot(x2);
		}
		int Count()
		{
			int count = 0;
			for (auto& e : _ufs)
			{
				if (e < 0)
					count++;
			}
			return count;
		}
	private:
		vector<int> _ufs;
	};
	template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
		typedef Graph<V,W, MAX_W, Direction> self;
	public:
		Graph() = default;
		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;
			}
			_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;
				}
			}
		}
		struct Edge
		{
			size_t _srci;
			size_t _dsti;
			W _w;
			Edge(size_t srci,size_t dsti,const W& w)
				:_srci(srci),_dsti(dsti),_w(w) {}
			bool operator>(const Edge& e) const
			{
				return _w > e._w;
			}
		};
		//从连通图中找最小生成树
		W Kruskal(self& minTree)
		{
			size_t n = _vertexs.size();
			minTree._vertexs = _vertexs;
			minTree._indexMap = _indexMap;
			minTree._matrix.resize(n);
			for (int i = 0; i < n; i++)
			{
				minTree._matrix[i].resize(n, MAX_W);
			}
			priority_queue<Edge, vector<Edge>, greater<Edge>> minq;
			for (int i = 0; i < n; i++)
			{
				for (int j = 0; j < n; j++)
				{
					//i < j 避免相同的边加入两次
					if (i < j && _matrix[i][j] != MAX_W)
					{
						minq.push(Edge(i, j, _matrix[i][j]));
					}
				}
			}
			//选出n-1条边:
			int size = 0;
			W totalw = W();
			UnionFindSet ufs(n);
			while (!minq.empty())
			{
				Edge min = minq.top();
				minq.pop();
				if (!ufs.InSet(min._srci, min._dsti))
				{
					minTree._addEdge(min._srci, min._dsti,min._w);
					ufs.Union(min._srci, min._dsti);
					++size;
					totalw += min._w;
				}
			}
			if (size == n - 1)
				return totalw;
			else
				return W();
		}
		W Prim(self& minTree, const W& src)
		{
			size_t srci = GetVertexIndex(src);
			size_t n = _vertexs.size();
			minTree._vertexs = _vertexs;
			minTree._indexMap = _indexMap;
			minTree._matrix.resize(n);
			for (int i = 0; i < n; i++)
			{
				minTree._matrix[i].resize(n, MAX_W);
			}
			vector<bool> X(n, false);
			vector<bool> Y(n, true);
			X[srci] = true;
			Y[srci] = false;
			priority_queue<Edge, vector<Edge>, greater<Edge>> minq;
			for (size_t i = 0; i < n; i++)
			{
				if (_matrix[srci][i] != MAX_W)
					minq.push(Edge(srci, i, _matrix[srci][i]));
			}
			size_t size = 0;
			W totalw = W();
			while (!minq.empty())
			{
				Edge min = minq.top();
				minq.pop();
				//最小边的目标点也在X集合则构成环
				if (X[min._dsti])
				{
					cout << "构成环:";
					cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
				}
				else
				{
					//添加边:
					minTree._addEdge(min._srci, min._dsti, min._w);
					cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;
					X[min._dsti] = true;
					Y[min._dsti] = false;
					++size;
					totalw += min._w;
					if (size == n - 1)
						break;
					for (size_t i = 0; i < n; i++)
					{
						if (_matrix[min._dsti][i] != MAX_W && Y[i])
							minq.push(Edge(min._dsti, i, _matrix[min._dsti][i]));
					}
				}
			}
			if (size == n - 1)
				return totalw;
			return W();
		}
	private:
		vector<V> _vertexs; //存储顶点的集合
		vector<vector<W>> _matrix; //存储边集合的矩阵
		map<V, int> _indexMap;//顶点映射下标
	};
	void TestGraphMinTree()
	{
		const char* str = "abcdefghi";
		Graph<char, int> g(str, strlen(str));
		g.AddEdge('a', 'b', 4);
		g.AddEdge('a', 'h', 8);
		g.AddEdge('b', 'c', 8);
		g.AddEdge('b', 'h', 11);
		g.AddEdge('c', 'i', 2);
		g.AddEdge('c', 'f', 4);
		g.AddEdge('c', 'd', 7);
		g.AddEdge('d', 'f', 14);
		g.AddEdge('d', 'e', 9);
		g.AddEdge('e', 'f', 10);
		g.AddEdge('f', 'g', 2);
		g.AddEdge('g', 'h', 1);
		g.AddEdge('g', 'i', 6);
		g.AddEdge('h', 'i', 7);
		Graph<char, int> kminTree;
		cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
		kminTree.Print();
		Graph<char, int> pminTree;
		cout << "Prim:" << g.Prim(pminTree,'a') << endl;
		pminTree.Print();
	}
}
int main()
{
	matrix::TestGraphMinTree();
	return 0;
}

Guess you like

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