Kruskal算法
Kruskal算法是一种用于求解最小生成树(Minimum Spanning Tree, MST)的贪心算法。最小生成树是指在一个带权无向图中,选取一棵包含所有顶点的树,使得树上所有边的权值之和最小。
算法步骤
1. 排序:将所有边按权值从小到大排序。
2. 初始化:创建一个空的森林(即每个顶点单独构成一棵树)。
3. 选择边:按顺序选择权值最小的边,如果这条边连接的两棵树不在同一棵树中(即不会形成环),则将其加入最小生成树中,并合并这两棵树。
4. 重复:重复步骤3,直到最小生成树中有 (V-1) 条边 (V) 是顶点数)。
伪代码
Kruskal(G): 1. 对图G的所有边按权值升序排序 2. 初始化一个空的森林F 3. 对于每条边(u, v) in G的边(按排序后的顺序): a. 如果u和v不在同一棵树中: i. 将边(u, v)加入F ii. 合并u和v所在的树 4. 返回F
实现细节
- 并查集(Disjoint Set Union, DSU):用于高效地判断两个顶点是否在同一棵树中,以及合并两棵树。
- 时间复杂度:排序边的时间复杂度为 O(E log E)),并查集操作的时间复杂度为 O(E log V),因此总时间复杂度为 O(E log E) 或 O(E log V)(因为 E <= V^2)。
应用场景
Kruskal算法常用于网络设计、电路设计、聚类分析等领域,适用于稀疏图(边数较少的情况)。
#include <stdio.h>
#include <stdlib.h>
// 定义边的结构体
struct Edge {
int src, dest, weight;
};
// 定义图的结构体
struct Graph {
int V, E; // V是顶点数,E是边数
struct Edge* edge; // 边的数组
};
// 创建图
struct Graph* createGraph(int V, int E) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*)malloc(E * sizeof(struct Edge));
return graph;
}
// 并查集的查找函数(带路径压缩)
int find(int parent[], int i) {
if (parent[i] == i)
return i;
return parent[i] = find(parent, parent[i]); // 路径压缩
}
// 并查集的合并函数(按秩合并)用于保证合并时的平衡性,避免树变得过高,从而提升 find 操作的效率。
void Union(int parent[], int rank[], int xroot, int yroot) { if (rank[xroot] < rank[yroot]) parent[xroot] = yroot; else if (rank[xroot] > rank[yroot]) parent[yroot] = xroot; else { parent[yroot] = xroot; rank[xroot]++; } } // 比较函数,用于边的排序 int compare(const void* a, const void* b) { struct Edge* a1 = (struct Edge*)a; struct Edge* b1 = (struct Edge*)b; return a1->weight - b1->weight; } // Kruskal算法实现 void KruskalMST(struct Graph* graph) { int V = graph->V; struct Edge result[V]; // 存储最小生成树的边 int e = 0; // 用于result数组的索引 int i = 0; // 用于排序后的边数组的索引 // 1. 对所有边按权值排序 qsort(graph->edge, graph->E, sizeof(graph->edge[0]), compare); // 初始化并查集 int* parent = (int*)malloc(V * sizeof(int)); int* rank = (int*)malloc(V * sizeof(int)); for (int v = 0; v < V; v++) { parent[v] = v; rank[v] = 0; } // 2. 选择边,直到最小生成树有 V-1 条边 while (e < V - 1 && i < graph->E) { // 选择权值最小的边 struct Edge next_edge = graph->edge[i++]; int xroot = find(parent, next_edge.src); int yroot = find(parent, next_edge.dest); // 如果不会形成闭环,则加入结果 if (xroot != yroot) { result[e++] = next_edge; Union(parent, rank, xroot, yroot); } } // 输出最小生成树的边 printf("最小生成树的边:\n"); for (i = 0; i < e; i++) printf("%d -- %d 权值: %d\n", result[i].src, result[i].dest, result[i].weight); // 释放内存 free(parent); free(rank); } int main() { int V = 4; // 顶点数 int E = 5; // 边数 struct Graph* graph = createGraph(V, E); // 添加边 graph->edge[0].src = 0; graph->edge[0].dest = 1; graph->edge[0].weight = 10; graph->edge[1].src = 0; graph->edge[1].dest = 2; graph->edge[1].weight = 6; graph->edge[2].src = 0; graph->edge[2].dest = 3; graph->edge[2].weight = 5; graph->edge[3].src = 1; graph->edge[3].dest = 3; graph->edge[3].weight = 15; graph->edge[4].src = 2; graph->edge[4].dest = 3; graph->edge[4].weight = 4; // 运行Kruskal算法 KruskalMST(graph); // 释放图的内存 free(graph->edge); free(graph); return 0; }
本文来自博客园,作者:z_s_s,转载请注明原文链接:https://www.cnblogs.com/zhoushusheng/p/18731768
浙公网安备 33010602011771号