• 算法提升:图的最小生成树算法-克鲁斯卡尔(Kruskal)


    目录

    概念

    思路

    代码


    概念

    克鲁斯卡尔算法查找最小生成树的方法是:将连通网中所有的边按照权值大小做升序排序,从权值最小的边开始选择,只要此边不和已选择的边一起构成环路,就可以选择它组成最小生成树。对于 N 个顶点的连通网,挑选出 N-1 条符合条件的边,这些边组成的生成树就是最小生成树。

    举个例子,图 1 是一个连通网,克鲁斯卡尔算法查找图 1 对应的最小生成树,需要经历以下几个步骤:
     


    图 1 连通网


    1) 将连通网中的所有边按照权值大小做升序排序:
     


    2) 从 B-D 边开始挑选,由于尚未选择任何边组成最小生成树,且 B-D 自身不会构成环路,所以 B-D 边可以组成最小生成树。
     


    图 2 B-D 边组成最小生成树


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


    图 3 D-T 边组成最小生成树


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


    图 4 A-C 边组成最小生成树


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


    图 5 C-D 边组成最小生成树


    6) C-B 边会和已选 C-D、B-D 边构成环路,因此不能组成最小生成树:
     


    图 6 C-B 边不能组成最小生成树


    7) B-T 、A-B、S-A 三条边都会和已选 A-C、C-D、B-D、D-T 构成环路,都不能组成最小生成树。而 S-A 不会和已选边构成环路,可以组成最小生成树。
     


    图 7 S-A 边组成最小生成树


    8) 如图 7 所示,对于一个包含 6 个顶点的连通网,我们已经选择了 5 条边,这些边组成的生成树就是最小生成树。
     


    图 8 最小生成树

    思路

    整个K算法中,其实简单的说就包含两个步骤:

    1. 找出路径中最短的边;
    2. 看这个边会不会对现有的边形成环路,如果会就丢弃,如果不会就记录;

    难点就是判断会不会有环,这里可以使用到并查集的思想去做,也可以直接使用一个集合,把所有的节点放入集合,如果下一次选中的边两个节点都在集合中可以被找到,那就说明会导致环路。

    代码

    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, elem, k;
    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 (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. elem = assists[end];
    41. //将新加入生成树的顶点标记全部改为一样的
    42. for (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, i;
    56. printf("最小生成树为:\n");
    57. for (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. }

  • 相关阅读:
    科创人·优艾智合创始人张朝辉:死磕细分行业Know-How,中国制造将引领全球移动机器人市场
    算法竞赛入门【码蹄集新手村600题】(MT1260-1280)C语言
    【流式细胞仪软件】上海道宁为您带来FCS Express,让您轻松缩小流式细胞术和结果之间的差距
    流批结合计算以及更多原生分析能力支持
    你有用过 java中的栈和队列吗?怎么用栈来实现队列呢
    《QT从基础到进阶·十五》用鼠标绘制矩形(QGraphicsView、QPainter、QGraphicsRectItem)
    瑞吉外卖 —— 13、项目优化:YApi、Swagger、项目部署
    Go语言开发k8s-05-ConfigMap操作
    【云原生】kubernetes学习之资源(对象)控制器概述---概念和实战(五)
    JS循环+数组
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/128073869