Kruskal 算法(最小生成树)
Kruskal 算法是一种用于求解加权连通图的最小生成树(MST)的贪心算法。它通过按权重从小到大排序所有边,并逐步选择不形成环的边,直到包含所有顶点。以下是详细的算法实现,包括原理、步骤、图示法表示步骤、代码关键行注释和时间复杂度。
1. 算法原理
Kruskal 算法的核心思想是贪心选择,即每次选择权重最小的边,确保不形成环。具体步骤如下:
初始化:
- 将所有边按权重从小到大排序。
- 初始化一个数组
v[]
,用于记录每个顶点的根节点。
贪心选择:
- 从权重最小的边开始,选择不形成环的边,将其加入生成树。
- 使用并查集(Union-Find)数据结构来检测环路。
重复步骤:
- 重复上述步骤,直到包含所有顶点。
2. 算法步骤
初始化:
- 将所有边按权重从小到大排序。
- 初始化数组
v[]
,每个顶点初始化为自己的根节点。
贪心选择:
- 从权重最小的边开始,选择不形成环的边,将其加入生成树。
- 使用并查集检测环路,如果两个顶点不在同一个集合中,则合并它们。
重复步骤:
- 重复上述步骤,直到包含所有顶点。
3. 图示法表示步骤
假设我们有以下加权无向图:
2 3
(0)--(1)--(2)
| / \ |
6| 8/ \5 |7
| / \ |
(3)--(4)--(5)
9 4
步骤 1:初始化
- 将所有边按权重排序:
[(0,1,2), (1,2,3), (2,5,4), (4,5,4), (0,3,6), (1,4,8), (3,4,9)]
- 初始化
v[]
数组:[0, 1, 2, 3, 4, 5]
步骤 2:贪心选择
选择边 (0,1,2):
a = 0
,b = 1
v[0] = 1
,sum = 2
选择边 (1,2,3):
a = 1
,b = 2
v[1] = 2
,sum = 5
选择边 (2,5,4):
a = 2
,b = 5
v[2] = 5
,sum = 9
选择边 (4,5,4):
a = 4
,b = 5
v[4] = 5
,sum = 13
选择边 (0,3,6):
a = 0
,b = 3
v[0] = 3
,sum = 19
选择边 (1,4,8):
a = 1
,b = 4
v[1] = 4
,sum = 27
最终结果
- 最小生成树的边为:
[(0,1,2), (1,2,3), (2,5,4), (4,5,4), (0,3,6)]
- 总权重为
19
4. 代码关键行注释
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义边结构体
struct Road {
int a, b, w;
};
// 获取顶点v的根节点
int getRoot(int v[], int j) {
while (v[j] != j) {
j = v[j];
}
return j;
}
// 快速排序
void QuickSort(Road road[], int low, int high) {
if (low < high) {
int i = low, j = high;
Road pivot = road[low];
while (i < j) {
while (i < j && road[j].w > pivot.w) --j;
road[i] = road[j];
++i;
while (i < j && road[i].w < pivot.w) ++i;
road[j] = road[i];
--j;
}
road[i] = pivot;
QuickSort(road, low, i - 1);
QuickSort(road, i + 1, high);
}
}
// Kruskal算法实现
void Kruskal(AMGraph* G, Road road[], int v[], int* sum) {
for (int i = 0; i < G->vexnum; i++) v[i] = i; // 初始化v数组
QuickSort(road, 0, G->arcnum - 1); // 对边按权重排序
*sum = 0; // 初始化最小生成树的边权重总和
for (int i = 0; i < G->arcnum; i++) {
int a = getRoot(v, road[i].a); // 获取顶点a的根节点
int b = getRoot(v, road[i].b); // 获取顶点b的根节点
if (a != b) { // 如果a和b不在同一个集合中
v[a] = b; // 合并a和b
*sum += road[i].w; // 更新最小生成树的边权重总和
}
}
}
5. 时间复杂度
- 时间复杂度:
- 排序时间复杂度为O(ElogE),其中 EE 是边数。
- 并查集操作的时间复杂度为O(ElogV),其中 VV 是顶点数。
- 总时间复杂度为O(ElogE)。
6. 总结
通过 Kruskal 算法,可以高效地求解加权连通图的最小生成树。该算法的时间复杂度为线性级别,适用于各种大小的图。