您的位置:首页 > 其它

最小生成树Prim算法和Kruskal算法

2016-09-20 10:36 465 查看

最小生成树(Minimum Cost Spanning Tree)

首先,最小生成树是一副连通加权无向图中一棵权值最小的生成树。

主要可以使用Prim和Kruskal算法实现,对于稀疏图来说,用Kruskal写最小生成树效率更好,加上并查集,可对其进行优化。

Kruskal算法(并查集实现)

在使用Kruskal实现最小生成树之前,先来看下并查集需要注意的两点:

1. 针对树可能会退化为链表的解决方案是,每次合并树时,总是将矮的树挂到高的树下,这种方式称为按秩合并。

2. 为了得到的树将更加扁平,加速以后直接或者间接引用节点的速度,Find时改变每一个节点的引用到根节点,这叫路径压缩。

并查集的初始化:

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

//Prim算法实现
void prim_test()
{
int n;
cin >> n;
vector<vector<int> > A(n, vector<int>(n));
for(int i = 0; i < n ; ++i) {
for(int j = 0; j < n; ++j) {
cin >> A[i][j];
}
}

int pos, minimum;
int min_tree = 0;
//lowcost数组记录每2个点间最小权值,visited数组标记某点是否已访问
vector<int> visited, lowcost;
for (int i = 0; i < n; ++i) {
visited.push_back(0);    //初始化为0,表示都没加入
}
visited[0] = 1;   //最小生成树从第一个顶点开始
for (int i = 0; i < n; ++i) {
lowcost.push_back(A[0][i]);    //权值初始化为0
}

for (int i = 0; i < n; ++i) {    //枚举n个顶点
minimum = max_int;
for (int j = 0; j < n; ++j) {    //找到最小权边对应顶点
if(!visited[j] && minimum > lowcost[j]) {
minimum = lowcost[j];
pos = j;
}
}
if (minimum == max_int)    //如果min = max_int表示已经不再有点可以加入最小生成树中
break;
min_tree += minimum;
visited[pos] = 1;     //加入最小生成树中
for (int j = 0; j < n; ++j) {
if(!visited[j] && lowcost[j] > A[pos][j]) lowcost[j] = A[pos][j];   //更新可更新边的权值
}
}

cout << min_tree << endl;
}

int main(void)
{
prim_test();

return 0;
}


View Code
注意:Prim算法实质就是每在最小生成树集合中加入一个点就需要把这个点与集合外的点比较,不断的寻找两个集合之间最小的边。

Kruskal VS Prim

方法上:Kruskal在所有边中不断寻找最小的边,Prim在U和V两个集合之间寻找权值最小的连接,共同点是构造过程都不能形成环。

时间上:Prim适合稠密图,复杂度为O(n * n),因此通常使用邻接矩阵储存,复杂度为O(e * loge),而Kruskal多用邻接表,稠密图 Prim > Kruskal,稀疏图 Kruskal > Prim。

空间上: Prim适合点少边多,Kruskal适合边多点少。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: