Kruskal算法
最后更新于:2022-04-01 20:06:17
**Kruskal算法**是求图最小生成树的一种算法,另外一种是[Prim算法](http://blog.csdn.net/u012796139/article/details/49865361)。
**Kruskal算法思想**:Kruskal算法按照边的权重(从大到小)处理它们,将边加入最小生成树中,加入的边不会对已经加入的边构成环,直到树中含有V-1条边为止。
**源代码**示例:
~~~
#include
#include
#include
#include
#include
using namespace std;
class Edge
{
public:
Edge(size_t _v, size_t _w, double _weight) : v(_v), w(_w), weight(_weight) {}
double getWeight() const { return weight; }
size_t either() const { return v; }
size_t other(size_t v) const
{
if (v == this->v)
return this->w;
else if (v == this->w)
return this->v;
else
{
cout << "error in Edge::other()" << endl;
exit(1);
}
}
void print() const{ cout << "(" << v << ", " << w << ", " << weight << ") "; }
private:
size_t v, w; // 两个顶点
double weight; // 权重
};
/// 加权无向图
class EdgeWeithtedGraph
{
public:
EdgeWeithtedGraph(size_t vertax_nums) : vertaxs(vertax_nums), edges(0), arr(vertax_nums) {}
void addEdge(const Edge e);
vector adj(size_t v) const { return v < arrSize() ? arr[v] : vector(); }
vector allEdges() const; // 返回加权无向图的所有边
size_t arrSize() const { return arr.size(); }
size_t vertax() const { return vertaxs; }
size_t edge() const { return edges; }
void printVertax(size_t v) const;
void printGraph() const;
private:
size_t vertaxs; // 顶点个数
size_t edges; // 边的个数
vector> arr; // 邻接表
};
/// 无向图数据结构,用于测试Kruskal算法中的边是否能构成环
class Graph
{
public:
Graph(size_t n) : arr(n), edges(0), vertaxs(n) {}
void addEdge(size_t a, size_t b)
{
if (!(a < arr.size() && b < arr.size()))
return;
arr[a].push_back(b);
arr[b].push_back(a);
edges++;
}
vector adj(size_t n) const
{
if (n < arr.size())
return arr[n];
else
return vector();
}
/// 返回顶点个数
size_t vertax() const { return arr.size(); }
private:
vector> arr; // 邻接表
size_t edges; // 边的个数
size_t vertaxs; // 顶点个数
};
/// 使用深度优先遍历判断Graph是否是无环图
class Cycle
{
public:
Cycle(const Graph &graph) : marked(graph.vertax()), has_cycle(false)
{
for (size_t i = 0; i < graph.vertax(); i++)
marked[i] = false;
for (size_t i = 0; i < graph.vertax(); i++)
{
if (!marked[i])
dfs(graph, i, i);
}
}
bool hasCycle() const { return has_cycle; }
private:
void dfs(const Graph &graph, size_t v, size_t u)
{
marked[v] = true;
vector vec = graph.adj(v);
for (size_t i = 0; i < vec.size(); i++)
{
if (!marked[vec[i]])
dfs(graph, vec[i], v);
else if (vec[i] != u) // 无环图其实就是树,此时v是树中一个节点,其父节点为u,当marked[vec[i]]==true时,表示v的父节点一定为u,否则图中有环
has_cycle = true;
}
}
vector marked;
bool has_cycle;
};
/// 判断最小生成树中是否有环,在Kruskal算法算法中被调用
bool hasCycle(vector vec, size_t nvextax)
{
if (vec.size() == 0)
return false;
Graph graph(nvextax);
for (size_t i = 0; i < vec.size(); i++)
{
size_t v = vec[i].either();
size_t w = vec[i].other(v);
graph.addEdge(v, w);
}
Cycle cycle(graph);
return cycle.hasCycle();
}
/// 为了存放Kruskal算法过程中的边,这些边按照权值从小到大排列
template
struct greator
{
bool operator()(const T &x, const T &y)
{
return x.getWeight() < y.getWeight();
}
};
/// 最小生成树的Kruskal算法
class KruskalMST
{
public:
KruskalMST(const EdgeWeithtedGraph &graph)
{
set> pq; // 计算过程保存边的set
vector vec = graph.allEdges();
set::iterator miniter;
for (size_t i = 0; i < vec.size(); i++) // 将所有的边存到pq中
pq.insert(vec[i]);
while (!pq.empty() && mst.size() < graph.arrSize() - 1) // graph.arrSize()可替换为变量
{
miniter = pq.begin();
mst.push_back(*miniter);
if (hasCycle(mst, graph.arrSize()))
removeEdge(*miniter);
pq.erase(miniter);
}
}
vector edges() const { return mst; }
void printEdges() const
{
for (size_t i = 0; i < mst.size(); i++)
{
mst[i].print();
}
cout << endl;
}
private:
void removeEdge(Edge e)
{
size_t v = e.either();
size_t w = e.other(v);
for (size_t i = 0; i < mst.size(); i++)
{
size_t iv = mst[i].either();
size_t iw = mst[i].other(iv);
if (v == iv && w == iw)
{
mst.erase(mst.begin() + i);
return;
}
}
}
vector mst;
};
int main(void)
{
EdgeWeithtedGraph graph(8);
graph.addEdge(Edge(0, 7, 0.16));
graph.addEdge(Edge(0, 2, 0.26));
graph.addEdge(Edge(0, 4, 0.38));
graph.addEdge(Edge(0, 6, 0.58));
graph.addEdge(Edge(1, 7, 0.19));
graph.addEdge(Edge(5, 7, 0.28));
graph.addEdge(Edge(2, 7, 0.34));
graph.addEdge(Edge(4, 7, 0.37));
graph.addEdge(Edge(1, 3, 0.29));
graph.addEdge(Edge(1, 5, 0.32));
graph.addEdge(Edge(1, 2, 0.36));
graph.addEdge(Edge(2, 3, 0.17));
graph.addEdge(Edge(6, 2, 0.40));
graph.addEdge(Edge(3, 6, 0.52));
graph.addEdge(Edge(4, 5, 0.35));
graph.addEdge(Edge(6, 4, 0.93));
cout << "arrSize: " << graph.arrSize() << endl;
cout << "vertax: " << graph.vertax() << endl;
cout << "edge: " << graph.edge() << endl;
cout << "----------------" << endl;
graph.printGraph();
cout << endl;
// 输出无向图的所有边
vector vec = graph.allEdges();
for (size_t i = 0; i < vec.size(); i++)
vec[i].print();
cout << endl << endl;
// Kruskal算法
KruskalMST prim(graph);
prim.printEdges();
system("pause");
return 0;
}
void EdgeWeithtedGraph::addEdge(const Edge e)
{
size_t v = e.either();
size_t w = e.other(v);
if (!(v < arrSize() && w < arrSize()))
return;
arr[v].push_back(e);
arr[w].push_back(e);
this->edges++;
}
vector EdgeWeithtedGraph::allEdges() const
{
vector vec;
for (size_t i = 0; i < arrSize(); i++)
{
for (size_t j = 0; j < arr[i].size(); j++)
{
if (arr[i][j].other(i) > i) // 所有边的权重各不不同,可以这样判断,每个边只保留一个
vec.push_back(arr[i][j]);
}
}
return vec;
}
void EdgeWeithtedGraph::printVertax(size_t v) const
{
if (v >= arrSize())
return;
for (size_t i = 0; i < arr[v].size(); i++)
arr[v][i].print();
cout << endl;
}
void EdgeWeithtedGraph::printGraph() const
{
for (size_t i = 0; i < arrSize(); i++)
{
cout << i << ": ";
printVertax(i);
}
}
~~~
**参考资料**:
1、[https://github.com/luoxn28/algorithm_data_structure](https://github.com/luoxn28/algorithm_data_structure) (里面有常用的数据结构源代码,图相关算法在graph文件夹中)
2、《算法 第4版》 图的Prim算法章节
3、[Prim算法](http://blog.csdn.net/u012796139/article/details/49865361)
';