赞
踩
一个图中有N个顶点,边的数量一定是>=N-1,我们从中选取N-1条边,用来连接N个点,所形成的边权之和最小,就是最小生成树。
构成最小生成树的准则
构成最小生成树的的方法:Kruskal(克鲁斯卡尔)算法和Prim(普里姆)算法
二者都是基于逐步贪心的算法
Kruskal算法的思路
描述这一过程,构成最小生成树
再选边时 都会构成环,此时已经是n-1条边,连接n个顶点,是最小生成树。
- 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;
- }
- bool operator<(const Edge& e) const
- {
- return _w < e._w;
- }
-
- };
-
- //最小生成树
- W Kruskal(Self& minTree)
- {
- size_t n = _vertexs.size();
- minTree._vertexs = _vertexs;
- minTree._vIndexMap = _vIndexMap;
- minTree._matrix.resize(n);
- for (size_t i = 0; i < minTree._vertexs.size(); i++)
- {
- minTree._matrix[i].resize(n, MAX_W);
- }
-
- //优先级队列
- priority_queue<Edge, vector<Edge>, greater<Edge>> minque;
- //把所有的边都放进去
- for (size_t i = 0; i < _matrix.size(); i++)
- {
- for (size_t j = 0; j < _matrix[i].size(); j++)
- {
- if (i<j&&_matrix[i][j] != MAX_W)
- {
- minque.push(Edge(i, j, _matrix[i][j]));
- }
- }
- }
-
- //选边
- W total = W();
- size_t i = 1;
- UnionFindSet ufs(_vertexs.size());
- while (!minque.empty())
- {
- Edge minedge = minque.top();
- minque.pop();
-
- if (!ufs.InSet(minedge._srci,minedge._dsti))
- {
- cout << _vertexs[minedge._srci] << "->" << _vertexs[minedge._dsti] << ":" << minedge._w << endl;
- ufs.Union(minedge._srci, minedge._dsti);
-
- minTree._AddEdge(minedge._srci, minedge._srci, minedge._w);
-
- total += minedge._w;
- ++i;
- }
-
- else
- {
- cout << "构成环" << endl;
- }
- }
-
- if (i == _vertexs.size())
- {
- return total;
- }
-
- return W();
- }

对于自定义类型的优先级队列,需要自定比较函数。
这里也运用到并查集的知识。
算法的基本思路
步骤
- W Prim(Self& minTree, const W& src)
- {
- size_t srci = GetVertexIndex(src);
- size_t n = _vertexs.size();
-
- minTree._vertexs = _vertexs;
- minTree._vIndexMap = _vIndexMap;
- minTree._matrix.resize(n);
- for (size_t i = 0; i < minTree._vertexs.size(); 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>> minque;
- //把所有的边都放进去
- for (size_t i = 0; i < _matrix.size(); i++)
- {
- if ( _matrix[srci][i] != MAX_W)
- {
- minque.push(Edge(srci, i, _matrix[srci][i]));
- }
- }
-
-
- cout << "Prim 选边" << endl;
- W total = W();
- size_t i = 1;
- while (!minque.empty())
- {
- Edge minedge = minque.top();
- minque.pop();
-
- if (X[minedge._dsti])
- {
- //cout << "构成环";
- }
- else
- {
- minTree._AddEdge(minedge._srci, minedge._dsti, minedge._w);
- X[minedge._dsti] = true;
- Y[minedge._dsti] = false;
- ++i;
- total += minedge._w;
-
- if (i == n) break;
-
- //将dsti相邻的都放到队列中
- for (size_t index = 0; index < n; index++)
- {
- if (Y[index]&&_matrix[minedge._dsti][index] != MAX_W)
- {
- minque.push(Edge(minedge._dsti, index,_matrix[minedge._dsti][index]));
- }
- }
- }
- }
- if (i == n)
- {
- return total;
- }
- return W();
- }

画图演示这个过程
........省略几步类似的步骤
选择 i-g i-h a-h时都会成环,不操作
最终的结果
最后依旧需要判断,如果完成n-1次选边后,可以构成最小生成树
否则,无法构成
Prim算法每次选边都会遍历相邻的边,是时间复杂度较大的算法。
Kruskal是全局贪心,每次选边都是选择最小的边。
Prim算法是局部贪心,总是选择目前相连的最小边。
二者所得到的权值是一样的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。