(一)Kuskal算法
【算法简介】:上一篇中的Prime算法是一种“加点式的算法”,而Kuskal算法是一种“加边式的算法”;Kuskal算法与Prime算法都是一种贪心算法,但Kruskal算法对图中存在相同权值的边时也有效。
【算法思想】:算法对权值从小到大排序,每次选取当前权值最小的边加入结点,直到所有的结点都已加入结点。算法中用到了并查集的思想(并查集),通过并查集来判断两个结点是否有共同的父节点,如果有,则表明两个结点已经联通。如果没有,就将两个结点联通,记录路径。
1.1 存图方式
使用结构体数组来存图;
struct node
{
int a; //边的起点
int b; //边的终点
int p; //边的权值,即:两结点点之间的距离
}mp[N]; //定义一个结构体数组来存图;
1.2 并查集思想(重点)
分为三部分:
(1)初始化:使每个结点的初始根节点为自己,并且每个结点构成一颗树,树的深度是1;
(2)查找:使用递归来查找每个结点的父亲结点;
(3)合并:将不同父节点的结点合并;
注:这里的并查集是优化后的,即:进行了路径压缩。如果题目中无要求,可以只写简单的并查集算法。
void inin(int n)
{
int i;
for (i = 0; i <= n; i++)
{
par[i] = i; //刚开始,每个数的跟节点都是自己
rank1[i] = 1; //刚开始树的深度为1;
}
}
int find(int x) //找每个结点的父亲结点;
{
if (x == par[x])
return x;
else
{
return par[x] = find(par[x]);
}
}
bool join(int a,int b) //将两个不同父节点的结点合并
{
int fa;
int fb;
fa = find(a);
fb = find(b);
if (fa == fb)
return false;
else if (rank1[fa] > rank1[fb])
{
par[fb] = fa;
}
else
{
if (rank1[fa] == rank1[fb])
{
rank1[fb]++;
}
par[fa] = fb;
}
return true;
}
1.3 Kuskal算法主体
变量每个结点;
int kruskal(int m)
{
int i;
sort(mp, mp + m, cmp);
for (i = 0; i < m; i++)
{
if (find(mp[i].a) != find(mp[i].b))//如果两个结点的父亲结点不一样,就合并。
{
join(mp[i].a, mp[i].b);
sum = sum + mp[i].p;
}
}
return sum;
}
Kuskal算法完整代码实现:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000
int par[N]; //记录父亲结点
int rank1[N]; //树的深度;
int sum; //最短路径
struct node
{
int a; //边的起点
int b; //边的终点
int p; //边的权值,即:两结点点之间的距离
}mp[N]; //定义一个结构体数组来存图;
bool cmp(node a,node b)
{
return a.p < b.p;
} //对边的权值进行从小到大排序
void inin(int n)
{
int i;
for (i = 0; i <= n; i++)
{
par[i] = i; //刚开始,每个数的跟节点都是自己
rank1[i] = 1; //刚开始树的深度为1;
}
}
int find(int x) //找每个结点的父亲结点;
{
if (x == par[x])
return x;
else
{
return par[x] = find(par[x]);
}
}
bool join(int a,int b) //将两个不同父节点的结点合并
{
int fa;
int fb;
fa = find(a);
fb = find(b);
if (fa == fb)
return false;
else if (rank1[fa] > rank1[fb])
{
par[fb] = fa;
}
else
{
if (rank1[fa] == rank1[fb])
{
rank1[fb]++;
}
par[fa] = fb;
}
return true;
}
int kruskal(int m)
{
int i;
sort(mp, mp + m, cmp);
for (i = 0; i < m; i++)
{
if (find(mp[i].a) != find(mp[i].b))//如果两个结点的父亲结点不一样,就合并。
{
join(mp[i].a, mp[i].b);
sum = sum + mp[i].p;
}
}
return sum;
}
int main()
{
int n, m;
cin >> n>>m;
inin(m);
int i;
for (i = 0; i < m; i++)
{
cin >> mp[i].a >> mp[i].b >> mp[i].p;
}
sum = 0;
kruskal(m);
cout << sum << endl;
return 0;
}
输入样例1:5个点,8条边,
5 8
1 2 3
1 3 7
2 3 10
2 4 4
2 5 8
3 4 6
3 5 2
4 5 17
输出:
输入样例2:9个点,14条边,
9 14
1 2 4
2 3 8
3 4 7
4 5 9
5 6 10
6 7 2
7 8 1
8 9 7
2 8 11
3 9 2
7 9 6
3 6 4
4 6 14
1 8 8
输出:37