• 克鲁斯卡尔算法


    连通图中寻找最小生成树的常用算法有 2 种,分别是普里姆算法和克鲁斯卡尔算法。本节,我们将带您详细了解克鲁斯卡尔算法。

    和普里姆算法类似,克鲁斯卡尔算法的实现过程也采用了贪心的策略:对于具有 n 个顶点的图,将图中的所有路径(边)按照权值大小进行升序排序,从权值最小的路径开始挑选,只要此路径不会和已选择的其它路径构成环路,就选定其作为最小生成树的一部分,直至选够 n-1 条路径。 

    对于具有 n 个顶点的图,选择 n-1 条路径就可以将所有顶点连接起来。在此基础上,保证所选的每条路径的权值都最小,就可以找到一棵最小生成树。

     以图 1 所示的连通图为例,克鲁斯卡尔算法寻找最小生成树的过程为:
    1) 将所有路径(边)按照权值大小进行升序排序:

    2) 从最小的路径开始,只要该路径不会和其它已选路径产生环路,就选择它作为组成最小生成树的一部分。显然 (B,D) 符合要求,选择它组成最小生成树:

    3) (D,T) 不会和已选路径 (B,D) 构成环路,可以组成最小生成树:

     4) (A,C) 不会和 (B,D)、(D,T) 构成环路,可以组成最小生成树:

    5) (C,D) 不会和 (A,C)、(B,D)、(D,T) 构成环路,可以组成最小生成树:

     6) (C,B) 会和已选路径 (C,D)、(B,D) 构成环路(如图 6 所示),因此不会被选择:

    7) (B,T) 会和已选路径 (B,D)、(D,T) 构成环路,也不被选择;

    8) (A,B) 会和已选路径 (A,C)、(C,D)、(D,B) 构成环路,也不被选择;

    9) (S,A) 不会和已选路径 (A,C)、(C,D)、(D,B)、(D,T) 构成环路,可以组成最小生成树:

     

    克鲁斯卡尔算法的具体实现

    克鲁斯卡尔算法的实现,难点在于如何判断所选路径是否会造成环路,这里给您介绍一种简单的解决方案:初始状态下,为图中的每个顶点配备一个互不相同的标记值,算法执行过程中,如果新路径两个顶点的标记值不同,则不会构成环路,该路径被选择的同时,要将两个顶点的标记改为和其它已选路径中的顶点标记相同;反之,如果该路径两个顶点的标记值相同,则会构成环路。

    举个例子,图 5 中,已选路径为 (A,C)、(B,D)、(C,D)、(D,T),此时顶点 A、C、B、D、T 的标记值相同,顶点 S 的标记值和它们不同。图 6 中,判定 (B,C) 路径是否可以组成最小生成树时,由于顶点 B 和 C 的标记值相同,因此该路径会和其它已选路径构成环路(如图 6 所示),不能组成最小生成树。

    如下为实现克鲁斯卡尔算法的 C 语言程序:

    1. #include
    2. #include
    3. #define N 9 // 图中边的数量
    4. #define P 6 // 图中顶点的数量
    5. //构建表示边的结构体
    6. struct edge {
    7. //一条边有 2 个顶点
    8. int initial;
    9. int end;
    10. //边的权值
    11. int weight;
    12. };
    13. //qsort排序函数中使用,使edges结构体中的边按照权值大小升序排序
    14. int cmp(const void *a, const void*b) {
    15. return ((struct edge*)a)->weight - ((struct edge*)b)->weight;
    16. }
    17. //克鲁斯卡尔算法寻找最小生成树,edges 存储用户输入的图的各个边,minTree 用于记录组成最小生成树的各个边
    18. void kruskal_MinTree(struct edge edges[], struct edge minTree[]) {
    19. int i,initial, end;
    20. //每个顶点配置一个标记值
    21. int assists[P];
    22. int num = 0;
    23. //初始状态下,每个顶点的标记都不相同
    24. for (i = 0; i < P; i++) {
    25. assists[i] = i;
    26. }
    27. //根据权值,对所有边进行升序排序
    28. qsort(edges, N, sizeof(edges[0]), cmp);
    29. //遍历所有的边
    30. for (int i = 0; i < N; i++) {
    31. //找到当前边的两个顶点在 assists 数组中的位置下标
    32. initial = edges[i].initial - 1;
    33. end = edges[i].end - 1;
    34. //如果顶点位置存在且顶点的标记不同,说明不在一个集合中,不会产生回路
    35. if (assists[initial] != assists[end]) {
    36. //记录该边,作为最小生成树的组成部分
    37. minTree[num] = edges[i];
    38. //计数+1
    39. num++;
    40. int elem = assists[end];
    41. //将新加入生成树的顶点标记全部改为一样的
    42. for (int k = 0; k < P; k++) {
    43. if (assists[k] == elem) {
    44. assists[k] = assists[initial];
    45. }
    46. }
    47. //如果选择的边的数量和顶点数相差1,证明最小生成树已经形成,退出循环
    48. if (num == P - 1) {
    49. break;
    50. }
    51. }
    52. }
    53. }
    54. void display(struct edge minTree[]) {
    55. int cost = 0;
    56. printf("最小生成树为:\n");
    57. for (int i = 0; i < P - 1; i++) {
    58. printf("%d-%d 权值:%d\n", minTree[i].initial, minTree[i].end, minTree[i].weight);
    59. cost += minTree[i].weight;
    60. }
    61. printf("总权值为:%d", cost);
    62. }
    63. int main() {
    64. int i;
    65. struct edge edges[N], minTree[P - 1];
    66. for (i = 0; i < N; i++) {
    67. scanf("%d %d %d", &edges[i].initial, &edges[i].end, &edges[i].weight);
    68. }
    69. kruskal_MinTree(edges,minTree);
    70. display(minTree);
    71. return 0;
    72. }
  • 相关阅读:
    【版本控制】Github和Gitlab同时使用ssh
    A Frustratingly Easy Approach for Entity and Relation Extraction 论文阅读
    Python的格式化输出
    vscode下载安装和配置使用
    信息熵原理与Python实现
    2022“杭电杯”中国大学生算法设计超级联赛(5)签到题3题
    [附源码]Python计算机毕业设计Django的低碳生活记录网站
    [02] Multi-sensor KIT: DSP 矩阵运算-加法,减法和逆矩阵,放缩,乘法和转置矩阵
    封装后台通用型页面及编辑弹窗实现思路与过程
    在 Vue 的 mounted 钩子函数中使用异步函数是否会有风险需要考虑
  • 原文地址:https://blog.csdn.net/RL2698853114/article/details/134272543