Kruskal算法

来源:互联网 发布:台达plc编程实例 编辑:程序博客网 时间:2024/06/07 19:53

       Kruskal算法是求图最小生成树的一种算法,另外一种是Prim算法。

       Kruskal算法思想:Kruskal算法按照边的权重(从大到小)处理它们,将边加入最小生成树中,加入的边不会对已经加入的边构成环,直到树中含有V-1条边为止。


源代码示例:

#include <iostream>#include <vector>#include <set>#include <algorithm>#include <functional>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<Edge> adj(size_t v) const { return v < arrSize() ? arr[v] : vector<Edge>(); }vector<Edge> 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<vector<Edge>> 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<size_t> adj(size_t n) const{if (n < arr.size())return arr[n];elsereturn vector<size_t>();}/// 返回顶点个数size_t vertax() const { return arr.size(); }private:vector<vector<size_t>> 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<size_t> 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<bool> marked;bool has_cycle;};/// 判断最小生成树中是否有环,在Kruskal算法算法中被调用bool hasCycle(vector<Edge> 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 <typename T>struct greator{bool operator()(const T &x, const T &y){return x.getWeight() < y.getWeight();}};/// 最小生成树的Kruskal算法class KruskalMST{public:KruskalMST(const EdgeWeithtedGraph &graph){set<Edge, greator<Edge>> pq; // 计算过程保存边的setvector<Edge> vec = graph.allEdges();set<Edge>::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<Edge> 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<Edge> 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<Edge> 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<Edge> EdgeWeithtedGraph::allEdges() const{vector<Edge> 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 (里面有常用的数据结构源代码,图相关算法在graph文件夹中)

       2、《算法 第4版》 图的Prim算法章节

       3、Prim算法


0 0
原创粉丝点击