最小生成树——Prim/Kruskal
来源:互联网 发布:全屏画图软件 编辑:程序博客网 时间:2024/05/20 06:26
本篇先用邻接矩阵示例。
假定图中有如下数据成员以及成员函数:
//邻接矩阵表示法#ifndef GRAPH_H#define GRAPH_H#include "edge.h"//template <typename T>class Graph {public: using T = char; ... void PrimMST(const int &index); //从点开始。 void KruskalMST(); //从边开始。private: const int& getWeight(const int &indexA, const int &indexB); struct Vertex { T data; bool isVisited; }; int capacity; //图中的顶点数 Vertex *pVertex; //顶点数组 int *pMatrix; //邻接矩阵 //边类,及边数组,为MST服务。将取出的边依次保存在pEdge中。 Edge *pEdge;};#endif
基本的建图、加点、加边、遍历,仅补充构造函数的实现,其余略过。
<span style="color:#800080;">Graph</span><span style="color:#000000;">::</span><span style="color:#000000;">Graph</span><span style="color:#000000;">(</span><span style="color:#808000;">const</span><span style="color:#c0c0c0;"> </span><span style="color:#808000;">int</span><span style="color:#c0c0c0;"> </span><span style="color:#000000;">&</span><span style="color:#000000;">capacity</span><span style="color:#000000;">):</span><pre style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style="color:#c0c0c0;"> </span><span style="color:#800000;">capacity</span><span style="color:#000000;">(</span><span style="color:#000000;">capacity</span><span style="color:#000000;">),</span>
pVertex(new Vertex[capacity]()),
pMatrix(new int[capacity * capacity]()),
pEdge(new Edge[capacity - 1]()) {} /*初始化列表,同时进行值初始化。*/
其中的Edge类,定义如下:
#ifndef EDGE_H#define EDGE_H#include <climits>struct Edge { Edge(const int &indexA=0, const int &indexB=0, const int &weight=INT_MAX); int indexA; int indexB; int weight;};bool operator<(const Edge &x, const Edge &y);#endif // EDGE_H
其实现文件重载了 <操作符,为生成最小堆服务。
#include "edge.h"Edge::Edge(const int &indexA, const int &indexB, const int &weight): indexA(indexA), indexB(indexB), weight(weight) {}bool operator<(const Edge &x, const Edge &y) { return x.weight > y.weight;}
Prim算法的主要原理:
1. 从一个给定的顶点出发,与之相邻的边为待选边;
2. 从待选边中选出权值最小的边;
3. 由最小边得到另一个顶点。若存在,形成回路,舍弃;否则,加入边集合,把另一个顶点加入点集合;
4. 重复1,直到 n-1 条边都被放入边集合(此时,n个点也都被放入了点集合)。
Kruskal算法的主要原理:
1. 将所有的边放入待选边集合;
2. 选出权值最小的边,放入已选边集合,同时把两个点放入对应的集合(点集可能有多个);
3. 重复2,若形成闭环,抛弃。直到所有的边(n-1)都纳入已选边集合中(同时,所有的点都在同一个点集中,非空点集只剩一个)。
实现文件如下:
void Graph::PrimMST(const int &index) { vector<int> vertexVec; //点集合 vertexVec.reserve(capacity); //预设容量为capacity priority_queue<Edge> edgeMinHeap; //备选边集合。 vertexVec.push_back(index); //加入第一个点,标记为已访问。 pVertex[index].isVisited = true; int edgeCnt(0); //已选边数量,初始化为0. while(edgeCnt < capacity - 1) { int tmp(vertexVec.back()); //新加入的顶点索引。 //遍历该点所有未选的边,加入备选边集合。 for(int i(0); i < capacity; ++i) { int weight(getWeight(tmp, i)); if(weight > 0 && !pVertex[i].isVisited) edgeMinHeap.emplace(Edge(tmp, i, weight)); //edgeMinHeap.emplace(tmp, i, weight); } //选出最小边,若构成回路(边的另一个顶点已被访问过),舍弃。 int nextVertexIndex(edgeMinHeap.top().indexB); if(!pVertex[nextVertexIndex].isVisited) { //加入顶点和边,并把顶点标记为已访问。 vertexVec.push_back(nextVertexIndex); pVertex[nextVertexIndex].isVisited = true; pEdge[edgeCnt++] = edgeMinHeap.top(); } edgeMinHeap.pop(); }}/* * 第一步:取出所有的边 * 第二步:从所有边中取出组成最小生成树的边*/void Graph::KruskalMST() { priority_queue<Edge> edgeMinHeap; for(int i(0); i < capacity; ++i) { //取出所有的边,放在一个最小堆中。 //取上三角或者下三角的数值都可以,这里遍历上三角。 for(int k(i + 1); k < capacity; ++k) { int weight(getWeight(i, k)); if(weight > 0) edgeMinHeap.emplace(Edge(i, k, weight)); } } vector<unordered_set<int>> vertexSets; //顶点集合的集合。 int edgeCnt(0); //已选边数,初始化为0 //1. 算法结束条件 while(edgeCnt < capacity - 1) { //2. 取出最小边 Edge minEdge(edgeMinHeap.top()); edgeMinHeap.pop(); //3. 找出最小边连接的点。 int indexA(minEdge.indexA); int indexB(minEdge.indexB); //4. 找出顶点所在的集合。 int ALabel(-1); //表示A点所在的集合的索引。 int BLabel(-1); for(unsigned i(0); i < vertexSets.size(); ++i) { if(ALabel == -1 && vertexSets[i].count(indexA) > 0) ALabel = i; if(BLabel == -1 && vertexSets[i].count(indexB) > 0) BLabel = i; } //5. 根据点所在的集合的不同,做不同的处理 if(ALabel == -1 && BLabel == -1) { //都不在已有的集合中,新建一个集合。 unordered_set<int> newSet; newSet.emplace(indexA); newSet.emplace(indexB); vertexSets.push_back(newSet); } else if(ALabel == -1 && BLabel != -1) //把A点加入B的集合中。 vertexSets[BLabel].emplace(indexA); else if(ALabel != -1 && BLabel == -1) //把B点加入A的集合中。 vertexSets[ALabel].emplace(indexA); else if(ALabel != BLabel) { //合并两个集合。 if(ALabel < BLabel) { //合并到靠前的集合,删除靠后的集合,效率高些。 vertexSets[ALabel].insert(vertexSets[BLabel].begin(), vertexSets[BLabel].end()); vertexSets.erase(vertexSets.begin() + BLabel); } else { vertexSets[BLabel].insert(vertexSets[ALabel].begin(), vertexSets[ALabel].end()); vertexSets.erase(vertexSets.begin() + ALabel); } } else continue; //在同一个集合的情况,说明这条边构成了回路,舍弃。 pEdge[edgeCnt++] = minEdge; }}
邻接矩阵适合用于表示稠密图,邻接表适合用于表示稀疏图。
Prim算法从点开始,适合用于求出稠密图的最小生成树;
Kruskal算法从边开始,适合于求出稀疏图的最小生成树。
图篇的内容要详谈,刨去代码不算,随便写点就能有几千字。
挺费功夫的,暂时是没这精力了。
0 0
- 最小生成树——Prim/Kruskal
- 最小生成树——Prim、Kruskal、Sollin(Boruvka)
- 最小生成树——Kruskal算法 和 Prim算法
- 算法——最小生成树:Kruskal算法、Prim算法
- 最小生成树算法——prim,Kruskal
- 图结构练习——最小生成树(Prim+Kruskal)
- 简单图论-最小生成树—kruskal+prim
- POJ2349—最小生成树的Kruskal和Prim实现
- 最小生成树—kruskal算法和prim算法
- 最小生成树(MST)—prim和kruskal算法
- 最小生成树—Prim算法和Kruskal算法
- 最小生成树算法—Kruskal算法和Prim算法
- 最小生成树—Prim算法和Kruskal算法 (理解)
- Prim、Kruskal最小生成树
- 最小生成树---Prim---Kruskal
- 最小生成树 Kruskal&&Prim
- 最小生成树 Prim Kruskal
- Kruskal/prim--最小生成树
- Java String对象常用的方法
- flume学习(五)
- 数组的整数次方
- 激励函数简介 Tensorflow最简单的三层神经网络及matplotlib可视化 附激励函数常见类型
- FastDFS+nginx服务搭建与配置
- 最小生成树——Prim/Kruskal
- 万能的林萧说:我来告诉你,一个草根程序员如何进入BAT。
- 混合类型数据格式化输入
- matlab 画图添加图例时,改变图例中字体大小
- 超速判断
- 用天平找小球
- ns-3.24与ns-3.25的区别导致的代码不兼容问题
- Atitit apache 和guava的反射工具
- Atitit 图像处理--图像分类 模式识别 肤色检测识别原理 与attilax的实践总结