Kruskal算法主要对边进行贪心,经过变换还可以求最大生成树,还可以记录树的具体路径。
Kruskal算法基本思想:
初始状态时隐去图中所有边,这样图中每个顶点都自成一个连通块。
- 对所有边权从小到大排序。
- 按边权从小到大测试所有边,如果当前测试边所连接的两个结点不在同一个连通块中,则把这条测试边加入最小生成树中( 合并两个连通块 );否则,将这条测试边舍弃。
- 执行步骤2,直至最小生成树中的 边数等于 总结点数-1 或是 测试完所有边。
概况一下就是:每次选择图中最小边权的边,如果边的两个端点在不同的连通块中,就把这条边加入最小生成树中。
伪代码
Kruskal()
{
令最小生成树的边权之和为sum、最小生成树的当前边数为cnt;
将所有边按边权从小到大排序;
for(从小到大枚举所有边)
{
if(当前测试边的两个端点在不同的连通块中)
{
将该测试边加入最小生成树中;
sum += 测试边边权;
最小生成树的当前边数cnt加1;
当边数cnt等于结点数减1时结束循环;
}
}
return sum;
}
关于判断两结点是否处于同一连通块,可用并查集实现。
完整代码:(计算最小生成树的边权和)
struct edge
{
int u, v;
int w;
}E[maxm]; //存放边
int N, M, father[maxn]; //N为结点数,M为边数
bool cmp(const edge &a, const edge &b)
{
return a.w < b.w;
}
void init() //并查集的初始化
{
for (int i = 0; i < maxn; i++)
father[i] = i;
}
int findFather(int x) //查找根
{
int root = x;
while (father[root] != root)
root = father[root];
//路径压缩
while (father[x] != x)
{
int t = x;
x = father[x];
father[t] = root;
}
return root;
}
int Kruskal(int r)
{
int sum = 0,cnt=0;
sort(E,E+M,cmp);
init();
for (int i = 0; i < M; i++)
{
int Fu = findFather(E[i].u);
int Fv = findFather(E[i].v);
if (Fu != Fv)
{
father[Fv] = Fu;
sum += E[i].w;
cnt++:
if(cnt==N-1)
break;
}
}
//注意:若cnt最终不等于N-1,说明存在无法连通的结点
return sum;
}
PS. 摘自《算法笔记》