Kruskal算法解决的是带权重的无向图上连接所有顶点的耗费最小的生成树。给出Kruskal算法求解最小生成树的【问题分析】、【算法伪代码】、以及【并查集的实现及优化方法】。
【问题分析】
1. 先将所有边用一个结构体存起来
2. 将图中所有边按照权值进行排序,权值小的排在前面。
3. 按权值大小,选中权值较小的边,如果该边与集合S中的边没有构成回路,则将边放入集合S,否则丢弃。
【算法伪代码】
//kruskal算法
Priority_queue<edge> q
for i=1 to n,j=i+1 to n
if(w[i][j]!=0)
q.push(w[i][j])
cnt++
for i=1 to m
e=q.top()
q.pop()
u=e.u,v=e.v,d=e.d
x=findroot(u)
y=findroot(v)
if(unio (x,y))
cout<<u<<’ ’<<v<<’ ’<<endl
【并查集的实现及优化方法】
实现:
实现kruskal算法的第2步时,需要判断加入该边后是否与S中的边构成回路,需要用到并查集。并查集是一个数组parent,最刚开始将其初始化为-1,在实现kruskal算法时,先考虑在某一条边加入集合S之前,这条边的两个顶点是否在同一棵树上,即看这两个顶点的根节点是否相同。若不相同,则将此边放入集合S,且将这两个顶点放在同一棵树上。
我们通过parent数组进行寻找:在一开始,parent数组初始化为-1,他们不会在同一棵树上,但后面S集合会不断更新,parent数组也不断更新,有些节点就会放在同一棵树上,这样我们就可以借助parent判断这条边是否与集合S中的边构成回路了。
优化方法:
在查找一个顶点的根节点的时候,从此节点不断寻找他的父节点从而找到根节点的工作量很大。
因此,我将所有结点都直接连接到同一个根节点,那么每个节点的父节点就是根节点,找根节点的时间就会大大缩短。
伪代码:
//在初始化时parent定义为-1
for(int i=1;i<=n;i++)
parent[i]=-1;
//查找根节点
int findroot(int x)
{
if(parent[x]==-1) return x;//独立的点,根节点是他自己
else
{
while(parent[x]!=-1)
{
x=parent[x];
}
return x;//找到根节点
}
}
//并查集操作
int unio(int x,int y)
{
int rootx = findroot(x);
int rooty = findroot(y);
if (rootx == rooty) return 0;//合并失败 根节点在同一个集合
else
{
parent[rootx] = rooty;
return 1;//合并成功
}
}
代码实现:
#include <bits/stdc++.h>
using namespace std;
int n;
int cnt = 0;
int w[100][100];
int parent[100];
struct edge
{
int u, v, d;
bool operator <(const edge& a) const
{
return d > a.d;//权值小的排在前面
}
};
void Initialization()
{
cin >> n;
//将parent初始为-1,为并查集操作做准备
for (int i = 1; i <= n; i++)
{
parent[i] = -1;
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> w[i][j];
}
}
}
//寻找根节点
int findroot(int x)
{
if (parent[x] == -1) return x;//一个独立的点根节点是他自己
else
{
while (parent[x] != -1)
{
x = parent[x];
}
return x;//找到根节点
}
}
//并查集操作
int unio(int x, int y)
{
int rootx = findroot(x);
int rooty = findroot(y);
if (rootx == rooty) return 0;//合并失败 根节点在同一个集合 即 边在同一个连通分支
else
{
parent[rootx] = rooty;
return 1;//合并成功
}
}
priority_queue<edge> q;
//kruskal算法
void Kruskal()
{
for (int i = 1; i <= n; i++)//将边导入优先队列
{
for (int j = i + 1; j <= n; j++)
{
if (w[i][j] != 0)
{
q.push({ i,j,w[i][j] });
cnt++;
}
}
}
for (int i = 0; i < cnt; i++)
{
edge e = q.top();
q.pop();
int u = e.u;
int v = e.v;
int d = e.d;
int x = findroot(u);
int y = findroot(v);
if (unio(x, y))
{
cout << u << ' ' << v << ' '<< d << endl;
}
}
}
int main()
{
Initialization();
Kruskal();
}