一个有 n 个结点的 连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用 kruskal(克鲁斯卡尔)算法或 prim(普里姆)算法求出。
一、prim算法
由于prim算法每次都需要找出距离点集最近的点,故其时间复杂度与顶点数V有关,即为,以下是Prim算法的模板:
const int INF = 100000000;
const int maxv = 1000;
int n, G[maxv][maxv];//邻接矩阵存储图
bool vis[maxv];
int dis[maxv];
int prim(){
fill(dis, dis + maxv, INF);
fill(vis, vis + maxv, 0); //初始默认所有点独立
dis[0] = 0;//把点0加入集合
for (int i = 0; i <= n; i++){
int u = -1, MIN = INF;
for (int j = 0; j <= n; j++){
if (!vis[j] && dis[j] < MIN) { //如果这个点没有加入集合S,且到集合的距离更短
MIN = dis[j];//更新点V到集合S的最小值
u = j;//把点赋给u
}
}
if (u == -1) return -1; //图不连通
vis[u] = true;//如果找到了这个点,就把它加入集合S
for (int v = 0; v <= n; v++) dis[v] = min(dis[v], G[u][v]);//用新加入的点更新dis[]
}
}
二、Kruskal算法
Kuruskal算法由于需要判断一条边的两个顶点是否属于同一个连通块,这可以用并查集来判断,所以需要先补充并查集的知识
#define SIZE 13
int UFSets[SIZE];
void Initial(int S[]) {
for (int i = 0; i < SIZE; i++)
S[i] = -1;
}
int Find(int S[], int x) {
int root = x;
while (S[root] >= 0)root = S[root];
//路径压缩
while (x != root) {
int t = S[x];
S[x] = root;
x = t;
}
return root;
}
void Union(int S[], int Root1, int Root2) {
if (Root1 == Root2) return;
if (S[Root2] > S[Root1]) { //Root2结点数更少
S[Root1] += S[Root2];
S[Root2] = Root1;
} else {
S[Root2] += S[Root1];
S[Root1] = Root2;
}
}
以下是Kruskal算法的模板,时间复杂度为:,
要注意并查集的写法,不然容易造成死循环
const int maxv = 1000;
const int maxe = 300;
int UFSets[100]; //并查集大小
int n, G[maxv][maxv];//n个顶点
int Enum = 0;//边的总数
struct edge {
int from;
int to;
int cost;
}E[maxe];
bool cmp(edge a, edge b) {
return a.cost < b.cost;
}
int Find(int S[], int x) {
int root = x;
while (S[root] >= 0)root = S[root];
//路径压缩
while (x != root) {
int t = S[x];
S[x] = root;
x = t;
}
return root;
}
int Kruskal() {
int num_edge = 0; //n+1个顶点最小生成树应该有n条边
fill(UFSets, UFSets + n + 1, -1); //fill(10):a[0]-a[9]
sort(E, E + Enum, cmp);//所有边按权从小到大排序
for (int i = 0; i < Enum; i++) {
int a = Find(UFSets, E[i].from);
int b = Find(UFSets, E[i].to);
if (a != b) { //不在同一个连通块中
UFSets[a] = b; //将其放入同一个连通块中
num_edge++;
if (num_edge == n)break; //最小生成树创建完成
}
}
if (num_edge != n) return -1; //图不连通返回-1
else return 1;
}