最小生成树通俗易懂的讲就是包含原图的所有节点而只用最少的边和最小的权值距离
最小生成树的理解:最小+树
最小:一个图,可以有很多生成树,我们把一棵树的权值相加,得到权值和。因此不同的生成树就会有不同的权值和,而最小生成树就是权值和最小生成树。
树:1、该图是树状结构,不会出现被圈起来的形状
2、对于N条顶点,有N-1条边
Kruskal算法(克鲁斯卡尔),直接选择权值最小的边
首先给一个这样全部连接的图,然后按照权值由大到小排列 ,如下图
然后按照上图权值由小到大将代表其权值的点连接起来,如果成功连接则边加1,如果出现圈的形状则将那条边去掉,然后下一条,最后得到的就是最小生成树
首先定义了一个结构体 Edge
,用于表示图中的边,包括边的起点、终点和权重。然后定义了一个结构体 DisjointSet
,用于表示并查集,其中包含了父节点和秩(rank)数组。
接着,我们实现了三个基本的并查集操作函数:
makeSet
:用于初始化并查集,将每个节点的父节点指向自身,并将秩初始化为 0。find
:用于查找节点 x 的根节点,并在查找的过程中进行路径压缩,即将节点 x 到根节点的路径上的所有节点直接连接到根节点,减少查找时间。unionSets
:用于合并两个集合,首先找到两个节点的根节点,然后根据秩的大小决定将一个根节点连接到另一个根节点上,并更新秩。
接下来,我们实现了一个比较函数 compare
,用于排序边的权重。然后在 kruskal
函数中,首先初始化并查集,然后对边按权重进行排序。在循环中,我们逐个考虑排序后的边,如果选取的边数等于节点数减一,则停止循环。对于每条边,我们找到起点和终点的根节点,如果两个根节点不相等,则选择该边,并将两个节点所在的集合合并。最终,输出所选取的边。
在 main
函数中,我们首先输入图的顶点数和边数,然后输入每条边的起点、终点和权重。最后调用 kruskal
函数进行 Kruskal 算法的执行
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
#define MAX_EDGES 100
// 边的结构体
typedef struct {
int start;
int end;
int weight;
} Edge;
// 并查集结构体
typedef struct {
int parent[MAX_VERTICES];
int rank[MAX_VERTICES];
} DisjointSet;
// 初始化并查集
void initializeDisjointSet(DisjointSet *ds, int n) {
for (int i = 0; i < n; i++) {
ds->parent[i] = i;
ds->rank[i] = 0;
}
}
// 查找节点的根节点(路径压缩)
int find(DisjointSet *ds, int x) {
if (ds->parent[x] != x) {
ds->parent[x] = find(ds, ds->parent[x]);
}
return ds->parent[x];
}
// 合并两个集合
void unionSets(DisjointSet *ds, int x, int y) {
int rootX = find(ds, x);
int rootY = find(ds, y);
if (ds->rank[rootX] > ds->rank[rootY]) {
ds->parent[rootY] = rootX;
} else if (ds->rank[rootX] < ds->rank[rootY]) {
ds->parent[rootX] = rootY;
} else {
ds->parent[rootY] = rootX;
ds->rank[rootX]++;
}
}
// 比较函数,用于排序边
int compare(const void *a, const void *b) {
return ((Edge *)a)->weight - ((Edge *)b)->weight;
}
// Kruskal算法
void kruskal(Edge edgeArray[], int n, int m) {
DisjointSet ds;
initializeDisjointSet(&ds, n);
qsort(edgeArray, m, sizeof(Edge), compare);
int selectedEdges = 0;
int i = 0;
for (i = 0; i < m; i++) {
if (selectedEdges == n - 1) {
break;
}
Edge e = edgeArray[i];
int rootStart = find(&ds, e.start);
int rootEnd = find(&ds, e.end);
if (rootStart != rootEnd) {
printf("选择的边: %d -> %d,权重为 %d\n", e.start, e.end, e.weight);
unionSets(&ds, rootStart, rootEnd);
selectedEdges++;
}
}
}
int main() {
int n, m;
Edge edgeArray[MAX_EDGES];
printf("请输入顶点数和边数:");
scanf("%d %d", &n, &m);
printf("请输入每条边的起点、终点和权重:\n");
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &edgeArray[i].start, &edgeArray[i].end, &edgeArray[i].weight);
}
kruskal(edgeArray, n, m);
return 0;
}