前言:本文笔者主要解释最小生成树以及最小生成树的一个算法-->Kruskal算法
最小生成树:
连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树
就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。
若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边。
构造最小生成树的三大准则:
1. 只能使用图中的边来构造最小生成树
2. 只能使用恰好n-1条边来连接图中的n个顶点
3. 选用的n-1条边不能构成回路
构造最小生成树的方法:Kruskal算法和Prim算法。这两个算法都采用了逐步求解的贪心策略。
贪心算法:是指在问题求解时,总是做出当前看起来最好的选择。也就是说贪心算法做出的不是
整体 最优的的选择,而是某种意义上的局部最优解。贪心算法不是对所有的问题都能得到整体最优解。
Kruskal算法:
每次贪心(选最小权值的边)选定一条边之后,连接2个顶点之前,判断这2个顶点相连会不会构成环,如果不构成则连接,然后继续贪心,反之就直接放弃这条边,继续贪心下一条边重复上述判断操作,直到不构成环就相连。
在判断是否成环这里有个问题:如何判断2个顶点相连会成环?
首先暴力遍历效率太慢并且代码实现会过于复杂,回想一下之前笔者写的这篇:高阶数据结构-->图(上)-CSDN博客中,实现的并查集,用并查集判断2个顶点之间的集合关系,如果2个顶点在同一个集合则相连后必定构成环。
Kruskal算法代码实现:
/*最小生成树*/
struct Edge
{
int _srci;
int _dsti;
W _w;
Edge(const int srci,const int dsti,const W& w)
:_srci(srci)
,_dsti(dsti)
,_w(w)
{}
};
struct cmp
{
bool operator()(const Edge& px, const Edge& py)
{
return px._w > py._w;
}
};
W Kruskal(self& minTree)
{
minTree._vectex = _vectex;
minTree._indexMap = _indexMap;
priority_queue <Edge,vector<Edge>,cmp> q1;
int n = _vectex.size();
minTree._edges.resize(n);
for (int i = 0; i < n; i++)
{
minTree._edges[i].resize(n, WMAX);
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (_edges[i][j] != WMAX && i < j)
{
q1.push(Edge(i, j,_edges[i][j]));
}
}
}
int size = 0;
W weightSum = W();
unionFindSet ufs(n);
while (q1.size())
{
Edge front = q1.top();
q1.pop();
if (ufs.isInSet(front._srci, front._dsti) == false)
{
minTree._addEdges(front._srci, front._dsti, front._w);
ufs.Union(front._srci, front._dsti);
weightSum += front._w;
++size;
}
}
if (size == n - 1) return weightSum;
else return W();
}
加上图的完整代码:
template<class V, class W, W WMAX = INT_MAX, bool judgMent = false>
class Graph
{
typedef Graph<V, W, WMAX, judgMent> self;
public:
Graph() = default;
Graph(const V* vectex, size_t n)
{
_vectex.reserve(n);
for (int i = 0; i < n; i++)
{
_vectex.push_back(vectex[i]);
_indexMap[vectex[i]] = i;
}
_edges.resize(n);
for (int i = 0; i < n; i++)
{
_edges[i].resize(n, WMAX);
}
}
size_t getIndex(const V& queriedNum)
{
auto it = _indexMap.find(queriedNum);
if (it != _indexMap.end())
{
return it->second;
}
else
{
throw "输入的顶点错误!";
return -1;
}
}
void _addEdges(int targetSrc, int targetDes, const W& edge)
{
_edges[targetSrc][targetDes] = edge;
if (judgMent == false)//判断是否是无向图
{
_edges[targetDes][targetSrc] = edge;
}
}
void AddEdge(const V& src, const V& des, const W& edge)
{
size_t targetSrc = getIndex(src);
size_t targetDes = getIndex(des);
_addEdges(targetSrc, targetDes,edge);
}
/*最小生成树*/
struct Edge
{
int _srci;
int _dsti;
W _w;
Edge(const int srci,const int dsti,const W& w)
:_srci(srci)
,_dsti(dsti)
,_w(w)
{}
};
struct cmp
{
bool operator()(const Edge& px, const Edge& py)
{
return px._w > py._w;
}
};
W Kruskal(self& minTree)
{
minTree._vectex = _vectex;
minTree._indexMap = _indexMap;
priority_queue <Edge,vector<Edge>,cmp> q1;
int n = _vectex.size();
minTree._edges.resize(n);
for (int i = 0; i < n; i++)
{
minTree._edges[i].resize(n, WMAX);
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (_edges[i][j] != WMAX && i < j)
{
q1.push(Edge(i, j,_edges[i][j]));
}
}
}
int size = 0;
W weightSum = W();
unionFindSet ufs(n);
while (q1.size())
{
Edge front = q1.top();
q1.pop();
if (ufs.isInSet(front._srci, front._dsti) == false)
{
minTree._addEdges(front._srci, front._dsti, front._w);
ufs.Union(front._srci, front._dsti);
weightSum += front._w;
++size;
}
}
if (size == n - 1) return weightSum;
else return W();
}
private:
vector<V> _vectex;
map<V, int> _indexMap;
vector<vector<W>> _edges;
};
可以测试一下:
int main()
{
const char* str = "abcdefghi";
adjacencyMatrices::Graph<char, int> g(str, strlen(str));
g.AddEdge('a', 'b', 4);
g.AddEdge('a', 'h', 8);
//g.AddEdge('a', 'h', 9);
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);
adjacencyMatrices::Graph<char, int> kminTree;
cout << "Kruskal:" << g.Kruskal(kminTree) << endl;
return 0;
}
输出:
Prim算法和最短路径问题会单独开一章讲述,敬请期待!!