Kruskal最小生成树的简单实现
思路:
每次选择权值最小的边,使这条边两个顶点相互连通(如果两个顶点已经连通,则跳过)
直到所有顶点全部连通
判断两个顶点是否连通,需要用到并查集
一个集合有一个代表人,可以把集合看成一个家庭,代表人就是顶梁柱(root)
-
初始化:每个顶点,在只有自己的集合,这个集合代表人初始化为自己
-
并:如果两个顶点分别所在集合的代表人不相同,则合并
- a集合代表人认b集合代表人为老大,则a集合的其他顶点也认b集合代表人为老大
-
查:找到某个顶点所在集合的代表人
例如
- 初始化,有六个集合,分别是{v0},{v1},{v2},{v3},{v4},{v5},代表人分别是自己
- v0与v3的代表人不一样,则把v0,v3连通,合并到一个集合,认v3为代表人
- {v0},{v3} -> {v0,v3}(代表v3)
- 同理连通v1,v4,合并{v1},{v4} -> {v1,v4}(代表v4)
- 同理连通v2,v5,合并{v2},{v5} -> {v2,v5}(代表v5)
- v2,v3所在集合代表不一样,所以连通v2,v3,合并成{v0,v3,v2,v5}(代表v3)
- v3,v5所在集合代表都是v3,所以跳过
- 其他同理
并查集简单实现(UnionAndFind.h)
#include <iostream>
#include <queue>
using namespace std;
#define VexSum 10
#define MaxSize 6
int root[MaxSize];
//初始化根为自己
bool InitRoot()
{
for (int i = 0; i < MaxSize; i++)
{
root[i] = i;
}
return true;
}
int FindRoot(int n)
{
//如果根为自己
if (root[n] == n)
{
return n;
}
else
{
//递归找到自己的根
return FindRoot(root[n]);
}
}
bool Union(int a, int b)
{
//如果a的根为b的根,这说明a,b已连通
if (FindRoot(a) == FindRoot(b))
{
return true;
}
else
{
//a认b为爸爸
root[FindRoot(a)] = FindRoot(b);
return false;
}
}
Kruskal算法(Kruskal.cpp)
使用queue队列模块实现
#include "UnionAndFind.h"
typedef struct Vex
{
int v1, v2;
int weight;
} Vex;
typedef struct Kruskal
{
int MinWeight;
queue<Vex> q;
} Kruskal;
//初始化
bool Init(Kruskal &k)
{
//初始化root数组
InitRoot();
//创建队列
for (int i = 0; i < VexSum; i++)
{
Vex v;
cin >> v.weight >> v.v1 >> v.v2;
k.q.push(v);
}
//初始化最小生成树权值之和
k.MinWeight = 0;
return true;
}
bool KruskalFunction(Kruskal &k)
{
while (!k.q.empty())
{
//如果两点连通
if (Union(k.q.front().v1, k.q.front().v2))
{
printf("%d与%d已连通,所以跳过!\n", k.q.front().v1, k.q.front().v2);
}
else
{
printf("%d与%d还没有连通,所以连通!\n", k.q.front().v1, k.q.front().v2);
k.MinWeight += k.q.front().weight;
}
k.q.pop();
}
printf("最小生成树权值之和为:%d\n", k.MinWeight);
return true;
}
int main()
{
/*
1 0 3
2 2 5
3 1 4
4 2 3
4 3 5
5 0 2
5 1 3
6 0 1
6 3 4
6 4 5
*/
Kruskal k;
Init(k);
KruskalFunction(k);
}