最小生成树
教学克鲁斯卡尔算法,这里临时补一下关于最小生成树的知识
引入:什么是最小生成树?
生成树
两个关键词:连通,无回路 —极小连通子图
边和顶点关系:n-1 n,路径唯一
边推点条件不充要(只是必要,但不充分)
深度,广度优先
- T(G) 已经经过的
- B(G) 未经过的
最小生成树
连通权值最小(最小代价生成树)
建立数学模型
应用:建立通信网 建立城村道路 (费用可以作为权值)MST性质:
what is?
在生成树的构造过程中,图中n个顶点分属两个集合:
- 已落在生成树上的顶点集:U
- 尚未落在生成树上的顶点集:V-U接下来则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。
图片展示:
算法
- 克鲁斯卡尔(Kruskal)
- 普利姆
这里我主要讲的是前者
步骤:
- 首先将所有点添加到U中(即生成树点集上)
- 找边的权值最小的选择即可,但是直接选是比较麻烦的,可以创建一个数组去存储所有的边(随便使用一个排序,冒泡选择插入都是可以的)
过程中需要排查,如果出现环就对这条边舍去
- 依次类推,直至所有点都在一个连通分量上即可(这里可以用一个案例去演示)
演示案例:
使用iodraw所画
最后的结果:
但是最小生成树可以不唯一
适合: 稀疏图(选择边的方式)
时间复杂度:O(e loge) e是边数
比较:
普利姆算法就更适用于稠密图 (选择点的方式)
时间复杂度:O(n^2) n为顶点数
算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <stdio.h> #include <stdlib.h> #define N 9 #define P 6
struct edge { int initial; int end; int weight; };
int cmp(const void* a, const void* b) { return ((struct edge*)a)->weight - ((struct edge*)b)->weight; }
void kruskal_MinTree(struct edge edges[], struct edge minTree[]) { int i, initial, end, elem, k; int assists[P]; int num = 0; for (i = 0; i < P; i++) { assists[i] = i; } qsort(edges, N, sizeof(edges[0]), cmp); for (i = 0; i < N; i++) { initial = edges[i].initial - 1; end = edges[i].end - 1; if (assists[initial] != assists[end]) { minTree[num] = edges[i]; num++; elem = assists[end]; for (k = 0; k < P; k++) { if (assists[k] == elem) { assists[k] = assists[initial]; } } if (num == P - 1) { break; } } } }
void display(struct edge minTree[]) { int cost = 0, i; printf("最小生成树为:\n"); for (i = 0; i < P - 1; i++) { printf("%d-%d 权值:%d\n", minTree[i].initial, minTree[i].end, minTree[i].weight); cost += minTree[i].weight; } printf("总权值为:%d", cost); }
int main() { int i; struct edge edges[N], minTree[P - 1]; for (i = 0; i < N; i++) { scanf("%d %d %d", &edges[i].initial, &edges[i].end, &edges[i].weight); } kruskal_MinTree(edges, minTree); display(minTree); return 0; }
|