C++使用Kruskal和Prim算法实现最小生成树

很久以前就学过最小生成树之Kruskal和Prim算法,这两个算法很容易理解,但实现起来并不那么容易。最近学习了并查集算法,得知并查集可以用于实现上述两个算法后,我自己动手实现了最小生成树算法。

宏观上讲,Kruskal算法就是一个合并的过程,而Prim算法是一个吞并的过程,另外在Prim算法中还用到了一种数据结构——优先级队列,用于动态排序。由于这两个算法很容易理解,在此不再赘述。接下来给出我的源代码。

输入

第一行包含两个整数n和m,n表示图中结点个数,m表示图中边的条数;接下来m行,每一行包含三个整数u,v,w,表示途中存在一条边(u,v),并且其权重为w;为了便于调试,我的程序是从文件中输入数据的;

输出

输出程序选择的最小生成树的权值之和以及每一条边信息;

Kruskal算法:

#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;

struct Edge
{
 int u; //边连接的一个顶点编号
 int v; //边连接另一个顶点编号
 int w; //边的权值
 friend bool operator<(const Edge& E1, const Edge& E2)
 {
 return E1.w < E2.w;
 }
};
//创建并查集
void MakeSet(vector<int>& uset, int n)
{
 uset.assign(n, 0);
 for (int i = 0; i < n; i++)
 uset[i] = i;
}
//查找当前元素所在集合的代表元
int FindSet(vector<int>& uset, int u)
{
 int i = u;
 while (uset[i] != i) i = uset[i];
 return i;
}
void Kruskal(const vector<Edge>& edges, int n)
{
 vector<int> uset;
 vector<Edge> SpanTree;
 int Cost = 0, e1, e2;
 MakeSet(uset, n);
 for (int i = 0; i < edges.size(); i++) //按权值从小到大的顺序取边
 {
 e1 = FindSet(uset, edges[i].u);
 e2 = FindSet(uset, edges[i].v);
 if (e1 != e2) //若当前边连接的两个结点在不同集合中,选取该边并合并这两个集合
 {
  SpanTree.push_back(edges[i]);
  Cost += edges[i].w;
  uset[e1] = e2; //合并当前边连接的两个顶点所在集合
 }
 }
 cout << "Result:\n";
 cout << "Cost: " << Cost << endl;
 cout << "Edges:\n";
 for (int j = 0; j < SpanTree.size(); j++)
 cout << SpanTree[j].u << " " << SpanTree[j].v << " " << SpanTree[j].w << endl;
 cout << endl;
}
int main()
{
 ifstream in("data.txt");

 int n, m;
 in >> n >> m;
 vector<Edge> edges;
 edges.assign(m, Edge());
 for (int i = 0; i < m; i++)
 in >> edges[i].u >> edges[i].v >> edges[i].w;
 sort(edges.begin(), edges.end()); //排序之后,可以以边权值从小到大的顺序选取边
 Kruskal(edges, n);

 in.close();

 system("pause");
 return 0;
}

Prim算法:

#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <queue>
using namespace std;
struct Node
{
 int v;
 int w;
 Node(int a, int b) :v(a), w(b){}
};
struct Edge
{
 int u;
 int v;
 int w;
 Edge(int a, int b, int c) :u(a), v(b), w(c){}
 friend bool operator<(const Edge& E1, const Edge& E2)
 {
 return E1.w>E2.w;
 }
};
int n, m;
vector<list<Node>> Adj;
priority_queue<Edge> Q;

int FindSet(vector<int>& uset, int v)
{
 int i = v;
 while (i != uset[i]) i = uset[i];
 return i;
}

void Prim()
{
 int Cost = 0; //用于统计最小生成树的权值之和
 vector<Edge> SpanTree; //用于保存最小生成树的边
 vector<int> uset(n,0); //用数组来实现并查集
 Edge E(0, 0, 0);
 for (int i = 0; i < n; i++) uset[i] = i; //并查集初始化
 for (auto it = Adj[0].begin(); it != Adj[0].end(); it++)
 Q.push(Edge(0, it->v, it->w)); //根据Prim算法,开始时选取结点0,并将结点0与剩余部分的边保存在优先队列中
 //循环中每次选取优先队列中权值最小的边,并更新优先队列
 while (!Q.empty())
 {
 E = Q.top();
 Q.pop();
 if (0 != FindSet(uset, E.v))
 {
  Cost += E.w;
  SpanTree.push_back(E);
  uset[E.v] = E.u;
  for (auto it = Adj[E.v].begin(); it != Adj[E.v].end(); it++)
  if (0 != FindSet(uset, it->v)) Q.push(Edge(E.v, it->v, it->w));
 }
 }
 cout << "Result:\n";
 cout << "Cost: " << Cost << endl;
 cout << "Edges:\n";
 for (int j = 0; j < SpanTree.size(); j++)
 cout << SpanTree[j].u << " " << SpanTree[j].v << " " << SpanTree[j].w << endl;
 cout << endl;
}
int main()
{
 ifstream in("data.txt");

 int u, v, w;
 in >> n >> m;
 Adj.assign(n, list<Node>());
 for (int i = 0; i < m; i++)
 {
 in >> u >> v >> w;
 Adj[u].push_back(Node(v,w));
 Adj[v].push_back(Node(u,w));
 }
 Prim();

 in.close();

 system("pause");
 return 0;
}

就实现而言,Kruskal算法比Prim算法更容易,代码更易于理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Prim(普里姆)算法求最小生成树的思想及C语言实例讲解

    Prim 算法思想: 从任意一顶点 v0 开始选择其最近顶点 v1 构成树 T1,再连接与 T1 最近顶点 v2 构成树 T2, 如此重复直到所有顶点均在所构成树中为止. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以把边上的权值解释为线路的造价.则最小生成树表示使其造价最小的生成树. 构造网的最小生成树必须解决下面两个问题: 1.尽可能选取权值小的边,但不能构成回路: 2.选取n-1条恰当的边以连通n个顶点: MST性质:假设G=(V

  • 最小生成树算法之Prim算法

    本文介绍了最小生成树的定义,Prim算法的实现步骤,通过简单举例实现了C语言编程. 1.什么是最小生成树算法? 简言之,就是给定一个具有n个顶点的加权的无相连通图,用n-1条边连接这n个顶点,并且使得连接之后的所有边的权值之和最小.这就叫最小生成树算法,最典型的两种算法就是Kruskal算法和本文要讲的Prim算法. 2.Prim算法的步骤是什么? 这就要涉及一些图论的知识了. a.假定图的顶点集合为V,边集合为E. b.初始化点集合U={u}.//u为V中的任意选定的一点 c.从u的邻接结点中

  • 最小生成树算法C语言代码实例

    在贪婪算法这一章提到了最小生成树的一些算法,首先是Kruskal算法,实现如下: MST.h 复制代码 代码如下: #ifndef H_MST#define H_MST #define NODE node *#define G graph *#define MST edge ** /* the undirect graph start */typedef struct _node { char data; int flag; struct _node *parent;} node; typede

  • 使用C语言实现最小生成树求解的简单方法

    最小生成树Prim算法朴素版 有几点需要说明一下. 1.2个for循环都是从2开始的,因为一般我们默认开始就把第一个节点加入生成树,因此之后不需要再次寻找它. 2.lowcost[i]记录的是以节点i为终点的最小边权值.初始化时因为默认把第一个节点加入生成树,因此lowcost[i] = graph[1][i],即最小边权值就是各节点到1号节点的边权值. 3.mst[i]记录的是lowcost[i]对应的起点,这样有起点,有终点,即可唯一确定一条边了.初始化时mst[i] = 1,即每条边都是从

  • 详解图的应用(最小生成树、拓扑排序、关键路径、最短路径)

    1.最小生成树:无向连通图的所有生成树中有一棵边的权值总和最小的生成树 1.1 问题背景: 假设要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路.这时,自然会考虑这样一个问题,如何在最节省经费的前提下建立这个通信网.在每两个城市之间都可以设置一条线路,相应地都要付出一定的经济代价.n个城市之间,最多可能设置n(n-1)/2条线路,那么,如何在这些可能的线路中选择n-1条,以使总的耗费最少呢? 1.2 分析问题(建立模型): 可以用连通网来表示n个城市以及n个城市间可能设置的通信线

  • C++使用Kruskal和Prim算法实现最小生成树

    很久以前就学过最小生成树之Kruskal和Prim算法,这两个算法很容易理解,但实现起来并不那么容易.最近学习了并查集算法,得知并查集可以用于实现上述两个算法后,我自己动手实现了最小生成树算法. 宏观上讲,Kruskal算法就是一个合并的过程,而Prim算法是一个吞并的过程,另外在Prim算法中还用到了一种数据结构--优先级队列,用于动态排序.由于这两个算法很容易理解,在此不再赘述.接下来给出我的源代码. 输入 第一行包含两个整数n和m,n表示图中结点个数,m表示图中边的条数:接下来m行,每一行

  • python最小生成树kruskal与prim算法详解

    kruskal算法基本思路:先对边按权重从小到大排序,先选取权重最小的一条边,如果该边的两个节点均为不同的分量,则加入到最小生成树,否则计算下一条边,直到遍历完所有的边. prim算法基本思路:所有节点分成两个group,一个为已经选取的selected_node(为list类型),一个为candidate_node,首先任取一个节点加入到selected_node,然后遍历头节点在selected_node,尾节点在candidate_node的边,选取符合这个条件的边里面权重最小的边,加入到

  • C++示例详解Prim算法与优先队列

    目录 Prim算法 prim代码实现 优先队列 优先队列代码实现 自定义类型优先序列 贪心算法的本质是:一个问题的局部最优解,也是该问题的全局最优解. 最小生成树的最优子结构性质:假设一个无向图包含两部分A,B,其中A为最小生成树部分,B为剩余部分,则存在以下性质:该无向图中一个顶点在A部分,另一个顶点在B部分的边中,权值最小的边一定属于整个无向图的最小生成树,即部分最小权值是整个最小生成树的局部最有解,该性质符合贪心算法的特点. Prim算法 基于最小生成树的该性质,使用prim算法来求解最小

  • JS使用Prim算法和Kruskal算法实现最小生成树

    之前都是看书,大部分也是c++的实现,但是搞前端不能忘了JS啊,所以JS实现一遍这两个经典的最小生成树算法. 一.权重图和最小生成树 权重图:图的边带权重 最小生成树:在连通图的所有生成树中,所有边的权重和最小的生成树 本文使用的图如下: 它的最小生成树如下: 二.邻接矩阵 邻接矩阵:用来表示图的矩阵就是邻接矩阵,其中下标表示顶点,矩阵中的值表示边的权重(或者有无边,方向等). 本文在构建邻接矩阵时,默认Number.MAX_SAFE_INTEGER表示两个节点之间没有边,Number.MIN_

  • 浅谈算法之最小生成树Kruskal的Python实现

    目录 一.前言 二.树是什么 三.从图到树 四.解决生成问题 五.从生成树到最小生成树 六.实际问题与代码实现 七.结尾 一.前言 我们先不讲算法的原理,也不讲一些七七八八的概念,因为对于初学者来说,看到这些术语和概念往往会很头疼.头疼也是正常的,因为无端突然出现这么多信息,都不知道它们是怎么来的,也不知道这些信息有什么用,自然就会觉得头疼.这也是很多人学习算法热情很高,但是最后又被劝退的原因. 我们先不讲什么叫生成树,怎么生成树,有向图.无向图这些,先简单点,从最基本的内容开始,完整地将这个算

  • C#图表算法之最小生成树

    目录 1.原理 1.切分定理 2.贪心算法 2.加权无向图的数据类型 3.最小生成树 API 4.Prim 算法 数据结构 维护横切边的集合 实现 性能 5. Prim 算法的即时实现 6.Kruskal 算法 实现 加权图是一种为每条边关联一个权值或是成本的图模型.这种图能够自然地表示许多应用.在一幅航空图中,边表示航线,权值则可以表示距离或是费用.在这些情形中,最令人感兴趣的自然是将成本最小化.这里用加权无向图模型来解决最小生成树:给定一幅加权无向图,找到它的一棵最小生成树. 图的生成树是它

  • NetworkX之Prim算法(实例讲解)

    引言 Prim算法与Dijkstra的最短路径算法类似,它采用贪心策略.算法开始先把图中权值最小的边添加到树T中,然后不断把权值最小的边E(E的一个端点在T中,另一个在G-T中).当没有符合条件的E时算法结束,此时T就是G的一个最小生成树. NetworkX是一款Python的软件包,用于创造.操作复杂网络,以及学习复杂网络的结构.动力学及其功能. 本文借助networkx.Graph类实现Prim算法. 正文 Prim算法的代码 Prim def prim(G, s): dist = {} #

  • Dijkstra算法与Prim算法的异同案例详解

    目录 Dijkstra简述 Prim简述 异 同 思想 时间复杂度 Dijkstra特例 Dijkstra简述 Dijkstra算法用于构建单源点的最短路径树(MST)--即树中某个点到任何其他点的距离都是最短的.例如,构建地图应用时查找自己的坐标离某个地标的最短距离.可以用于有向图,但是不能存在负权值(Bellman-Ford可以处理负权值). 伪代码 Dijkstra() { for each u in G,V { //此处做初始化操作,给每个节点u赋键值+∞,设置空为父节点 u.key =

随机推荐