一、概述
最小生成树(MST, Minimum Spanning Tree)问题简单来说就是在多个地方修路路程的最短,
或者在多个选择中选择花费做少的路线。
求解最小生成树的算法的方法有
Prim算法,Kruskal算法,以及Reverse-Delete algorithm(反向删除算法)
比较而言
prim算法适合稠密图,kruskal算法适合简单图。
下面来用Kruskal算法实现最小生成树
二、Kruskal算法
伪代码如下
需要用到uniofind数据结构来维护图的边和顶点
简单来说这种数据结构可以用来维护多个集合,判断某个元素是否在某个集合里,以及完成集合间的合并
三、题目
数据输入:
第一行是2个整数,分别表示顶点个数n和边数m。接下来的m行中,每一行第一个整
数表示边的开始顶点,第二个表示边的结束顶点,第三个表示这条边的权重。
(
测试数据中保证图是连通图;
没有自环;
两个顶点之间只有一条边;
0<权重<100(可以相等); n<=50; m<=1000;
)
结果输出:
输出无向连通图最小生成树权重之和
代码如下,使用了平衡并查集来降低集合合并的复杂度
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// UnionFind base class
class UnionFind
{
public:
UnionFind(int n);
~UnionFind();
virtual bool Connected(int p, int q) = 0;
virtual void Union(int p, int q) = 0;
protected:
int *id;
int *sz;// store every vertex set's connect with how many vertexs
};
UnionFind::UnionFind(int n)
{
this->id = new int[n];
this->sz = new int[n];
for (int i = 0; i < n; i++){
// initialize every vertex set has only 1 vertex
this->id[i] = i;
this->sz[i] = 1;
}
};
UnionFind::~UnionFind()
{
delete this->id;
delete &this->sz;
};
// 平衡并查集
class QuickUnion : public UnionFind
{
public:
QuickUnion(int n);
bool Connected(int p, int q);
void Union(int p, int q);
protected:
int findRoot(int p);
};
QuickUnion::QuickUnion(int n) :UnionFind(n) {};
int QuickUnion::findRoot(int p)
{
while (p != this->id[p])
p = this->id[p];
return p;
};
bool QuickUnion::Connected(int p, int q)
{
return this->findRoot(p) == this->findRoot(q);
};
void QuickUnion::Union(int p, int q)
{
int rootp = this->findRoot(p);
int rootq = this->findRoot(q);
if (rootq == rootp)return;
//比较p属于的集合与q属于的集合的大小
if (this->sz[rootp]>this->sz[rootq])
{
//如果p的集合较大,我们将q的根结点的父节点设置为i的根结点
this->id[rootq] = this->id[rootp];
//更新合并后的新集合大小,一个集合的大小只保存在它的根结点
this->sz[rootp] += this->sz[rootq];
}
else
{
this->id[rootp] =this->id[rootq];
this->sz[rootq] += this->sz[rootp];
}
};
struct EVNode
{
int vertex1;
int vertex2;
int weight;
}EVUion;
// n : vertex number
// m : edge number
int n = 0, m = 0;
vector<EVNode> loadData() {
vector<EVNode> EV_Vector;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int v1, v2, w;
cin >> v1 >> v2 >> w;
EVUion.vertex1 = v1;
EVUion.vertex2 = v2;
EVUion.weight = w;
EV_Vector.push_back(EVUion);
}
return EV_Vector;
}
bool sortWeight( EVNode evNd1, EVNode evNd2) {
return evNd1.weight < evNd2.weight;
}
// Kruskal algorithm
vector<int> Kruskal(vector<EVNode> ev) {
vector<int> T;
// sort ev vector according to the weight
sort(ev.begin(), ev.end(), sortWeight);
cout << "sorted after" << endl;
for (int i = 0; i < m; i++)
cout << ev[i].vertex1 << " " << ev[i].vertex2 << " " << ev[i].weight << endl;
// make a set containing singleton vertex
QuickUnion *Vertex_union = new QuickUnion(n);
for (int i = 0; i < m; i++) {
// if ev[i].vertex1 ev[i].vertex2 in different set ,in other words
if (Vertex_union->Connected(ev[i].vertex1-1, ev[i].vertex2-1)!=true) {
// add this edge to the tree
T.push_back(ev[i].weight);
// margin the sets containing ev[i].vertex1 ev[i].vertex2
Vertex_union->Union(ev[i].vertex1-1, ev[i].vertex2-1);
}
}
return T;
}
int main() {
vector<EVNode> EV_Vector;
EV_Vector=loadData();
vector<int> T;
T = Kruskal(EV_Vector);
int sum = 0;
cout << "The edge of the minimum spanning tree is" << endl;
for (int i = 0; i < T.size(); i++)
{
cout << T[i] << endl;
sum += T[i];
}
cout << "sum="<<sum;
return 0;
}
四、结果测试
输入:
6 10 1 2 6 1 3 1 1 4 5 2 3 5 2 5 3 3 4 5 3 5 6 3 6 4 4 6 2 5 6 6
图形展示
输出:
结果正确 d=====( ̄▽ ̄*)b