• 图->连通性->最小生成树(克鲁斯卡尔算法)


    文字描述

      上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系;

      而用克鲁斯卡尔(Kruskal)算法求最小生成树则恰恰相反。它的时间复杂度为eloge (e为网中边的数目),因此它相对于普里姆算法而言,适合于求边稀疏的网的最小生成树。

      克鲁斯卡尔算法求最小生成树的步骤为:假设连通网N={V,{E}}, 则令最小生成树的初始状态为只有n个顶点而无边的非连通图 T=(V, {}}, 图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量中,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直至T中所有顶点都在同一连通分量上为止。

    示意图:

    算法分析:

      实现克鲁斯卡尔的话,可以借助于之前介绍的"堆"(堆排序)和树的等价划分的方法。用”堆”来存放网中的边,则每次选择最小代价的边仅需loge的时间(第一次需e)。又生成树T的每个连通分量可看成一个等价类,则构造T加入新的边的过程类似于求等价类的过程,由此可用MFSet类型来描述顶点,用堆Heap存放弧结点信息,使构造T的过程仅需eloge的时间,由此,克鲁斯

    代码实现

      1 //
      2 // Created by lady on 18-12-15.
      3 //
      4 
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #define MAX_NODE_NUM 20
     10 #define MAX_ARCH_NUM 30
     11 
     12 #define EQ(a, b) ((a)==(b))
     13 #define LT(a, b) ((a) <(b))
     14 #define LQ(a, b) ((a)<=(b))
     15 
     16 
     17 typedef struct PTNode{
     18     char data[10];
     19     int index;
     20     int parent;
     21 }PTNode;
     22 
     23 typedef struct{
     24     PTNode node[MAX_NODE_NUM+1];
     25     int n;
     26 }MFSet;
     27 
     28 typedef struct ArcBox{
     29     int vex1, vex2;
     30     int weight;
     31 }ArcBox;
     32 
     33 typedef  struct{
     34     ArcBox r[MAX_ARCH_NUM+1];
     35     int len;
     36 }HeapType;
     37 
     38 /* param1 S: S是已存在的集合
     39  * param2 index: index是S中某个子集的成员的下标值
     40  * result: 查找函数,确定S中位置为index所属子集Si,并返回其子集中的根结点的位置。
     41  */
     42 static int findMFSet(MFSet *S, int index)
     43 {
     44     if(index < 1 || index > S->n)
     45         return -1;
     46     int j = 0;
     47     for(j=index; S->node[j].parent>0; j=S->node[j].parent);
     48     return j;
     49 }
     50 
     51 /*
     52  * 集合S中位置为index_i和index_j所在的子集互不相交。
     53  * result: 将置为index_i和index_j所在的互不相交的子集合并为一个子集。
     54  */
     55 static int mergeMFSet(MFSet *S, int index_i, int index_j)
     56 {
     57     int loc_i = -1, loc_j = -1;
     58     if((loc_i=findMFSet(S, index_i)) < 0){
     59         return -1;
     60     }
     61     if((loc_j=findMFSet(S, index_j)) < 0){
     62         return -1;
     63     }
     64     if(loc_i == loc_j){
     65         return -1;
     66     }
     67     //结点数少的子集指向结点数大的子集
     68     if(S->node[loc_i].parent > S->node[loc_j].parent){
     69         S->node[loc_j].parent += S->node[loc_i].parent;
     70         S->node[loc_i].parent = loc_j;
     71     }else{
     72         S->node[loc_i].parent += S->node[loc_j].parent;
     73         S->node[loc_j].parent = loc_i;
     74     }
     75     return 0;
     76 }
     77 
     78 static int initialMFSet(MFSet *S, int n)
     79 {
     80     int i = 0;
     81     S->n = n;
     82     for(i=1; i<=S->n; i++)
     83     {
     84         printf("输入第%d个子集:", i);
     85         scanf("%s", S->node[i].data);
     86         S->node[i].parent = -1;
     87         S->node[i].index = i;
     88     }
     89     return 0;
     90 }
     91 
     92 /*
     93  * 返回结点中数据等于data的下标值
     94  */
     95 static int getLocaofVex(MFSet *S, char data[])
     96 {
     97     int i = 0;
     98     for(i=1; i<=S->n; i++){
     99         if(!strncasecmp(S->node[i].data, data, sizeof(S->node[0].data))){
    100             return S->node[i].index;
    101         }
    102     }
    103     return -1;
    104 }
    105 
    106 static void printMFSet(MFSet *S)
    107 {
    108     printf("打印MFSet结构中的数据:
    ");
    109     int i = 0;
    110     for(i=1; i<=S->n; i++){
    111         printf("index %d:(data %s, parent %d)
    ", S->node[i].index, S->node[i].data, S->node[i].parent);
    112     }
    113     printf("
    ");
    114 }
    115 
    116 
    117 
    118 //////////////////////////////////////////////
    119 
    120 static int printHeap(HeapType *H)
    121 {
    122     printf("打印堆结构中的数据:
    ");
    123     int i = 0;
    124     for(i=1; i<=H->len; i++){
    125         printf("index %d: arch:(%d,%d),weight %d)
    ", i, H->r[i].vex1, H->r[i].vex2, H->r[i].weight);
    126     }
    127     return 0;
    128 }
    129 
    130 static int initialHeap(HeapType *H, MFSet *S, int n)
    131 {
    132     H->len = n;
    133     int i = 0;
    134     char s1[10] = {0};
    135     char s2[10] = {0};
    136     char s3[10] = {0};
    137     int weight = 0;
    138 
    139     char tmp[30] = {0};
    140     for(i=1; i<=H->len; i++)
    141     {
    142         printf("输入第%d条弧信息(顶点1 顶点2 权值):", i);
    143         memset(tmp, 0, sizeof(tmp));
    144         scanf("%s", tmp);
    145         sscanf(tmp, "%[^','],%[^','],%s[^\n]", s1, s2, s3);
    146         H->r[i].vex1 = getLocaofVex(S, s1);
    147         H->r[i].vex2 = getLocaofVex(S, s2);
    148         H->r[i].weight = atoi(s3);
    149     }
    150     return 0;
    151 }
    152 
    153 /*
    154  *已知H->r[s,...,m]中记录的关键字除H->r[s].key之外均满足的定义
    155  *本函数调整H-r[s]的关键字,使H->r[s,...,m]成为一个小顶堆(对其中
    156  *记录的弧的权值而言)
    157  */
    158 void HeapAdjust(HeapType *H, int s, int m)
    159 {
    160     ArcBox rc = H->r[s];
    161     int j = 0;
    162     //沿weight较小的孩子结点向下筛选
    163     for(j=2*s; j<=m; j*=2){
    164         //j为weight较小的孩子结点下标
    165         if(j<m && (!LQ(H->r[j].weight, H->r[j+1].weight)))
    166             j+=1;
    167         if(LQ(rc.weight, H->r[j].weight))
    168             break;
    169         H->r[s] = H->r[j];
    170         s=j;
    171     }
    172     H->r[s] = rc;
    173 }
    174 
    175 void HeapSort(HeapType *H, MFSet *S)
    176 {
    177     int i = 0;
    178     printf("1)建立一个小顶堆!
    ");
    179     //把H->r[1,...,H->length]建成小顶堆
    180     for(i=H->len/2; i>=1; i--){
    181         HeapAdjust(H, i, H->len);
    182     }
    183     printHeap(H);
    184     printf("2)依次输出堆顶元素并重新调整成小顶堆!
    ");
    185     ArcBox tmp;
    186     for(i=H->len; i>1; i--){
    187         tmp = H->r[1];
    188         H->r[1] = H->r[i];
    189         H->r[i] = tmp;
    190         HeapAdjust(H, 1, i-1);
    191         printf("新堆顶的弧信息: (%d,%d) %d", tmp.vex1, tmp.vex2, tmp.weight);
    192         if(mergeMFSet(S, tmp.vex1, tmp.vex2) > -1){
    193             printf("	是最小生成树的弧!
    ");
    194         }else{
    195             printf("
    ");
    196         }
    197     }
    198 }
    199 
    200 
    201 
    202 
    203 int main(int argc, char *argv[])
    204 {
    205     int nodes=0;
    206     int archs=0;
    207     printf("输入顶点数和弧数:");
    208     scanf("%d,%d", &nodes, &archs);
    209 
    210     printf("
    以MFSet结构存放顶点信息:
    ");
    211     MFSet S;
    212     initialMFSet(&S, nodes);
    213     printMFSet(&S);
    214 
    215     printf("以堆结构存放弧信息:
    ");
    216     HeapType H;
    217     initialHeap(&H, &S, archs);
    218     printHeap(&H);
    219 
    220     printf("对存放了弧信息的堆进行排序,在排序过程中输入最小生成树的边
    ");
    221     HeapSort(&H, &S);
    222     return 0;
    223 }
    最小生成树(克鲁斯卡尔算法)

    代码运行

    /home/lady/CLionProjects/untitled/cmake-build-debug/untitled
    输入顶点数和弧数:6,10
    
    以MFSet结构存放顶点信息:
    输入第1个子集:V1
    输入第2个子集:V2
    输入第3个子集:V3
    输入第4个子集:V4
    输入第5个子集:V5
    输入第6个子集:V6
    打印MFSet结构中的数据:
    index 1:(data V1, parent -1)
    index 2:(data V2, parent -1)
    index 3:(data V3, parent -1)
    index 4:(data V4, parent -1)
    index 5:(data V5, parent -1)
    index 6:(data V6, parent -1)
    
    以堆结构存放弧信息:
    输入第1条弧信息(顶点1 顶点2 权值):V3,V1,1
    输入第2条弧信息(顶点1 顶点2 权值):V3,V2,5
    输入第3条弧信息(顶点1 顶点2 权值):V3,V5,6
    输入第4条弧信息(顶点1 顶点2 权值):V3,V6,4
    输入第5条弧信息(顶点1 顶点2 权值):V3,V4,5
    输入第6条弧信息(顶点1 顶点2 权值):V1,V2,6
    输入第7条弧信息(顶点1 顶点2 权值):V2,V5,3
    输入第8条弧信息(顶点1 顶点2 权值):V5,V6,6
    输入第9条弧信息(顶点1 顶点2 权值):V6,V4,2
    输入第10条弧信息(顶点1 顶点2 权值):V4,V1,5
    打印堆结构中的数据:
    index 1: arch:(3,1),weight 1)
    index 2: arch:(3,2),weight 5)
    index 3: arch:(3,5),weight 6)
    index 4: arch:(3,6),weight 4)
    index 5: arch:(3,4),weight 5)
    index 6: arch:(1,2),weight 6)
    index 7: arch:(2,5),weight 3)
    index 8: arch:(5,6),weight 6)
    index 9: arch:(6,4),weight 2)
    index 10: arch:(4,1),weight 5)
    对存放了弧信息的堆进行排序,在排序过程中输入最小生成树的边
    1)建立一个小顶堆!
    打印堆结构中的数据:
    index 1: arch:(3,1),weight 1)
    index 2: arch:(6,4),weight 2)
    index 3: arch:(2,5),weight 3)
    index 4: arch:(3,6),weight 4)
    index 5: arch:(3,4),weight 5)
    index 6: arch:(1,2),weight 6)
    index 7: arch:(3,5),weight 6)
    index 8: arch:(5,6),weight 6)
    index 9: arch:(3,2),weight 5)
    index 10: arch:(4,1),weight 5)
    2)依次输出堆顶元素并重新调整成小顶堆!
    新堆顶的弧信息: (3,1) 1    是最小生成树的弧!
    新堆顶的弧信息: (6,4) 2    是最小生成树的弧!
    新堆顶的弧信息: (2,5) 3    是最小生成树的弧!
    新堆顶的弧信息: (3,6) 4    是最小生成树的弧!
    新堆顶的弧信息: (4,1) 5
    新堆顶的弧信息: (3,4) 5
    新堆顶的弧信息: (3,2) 5    是最小生成树的弧!
    新堆顶的弧信息: (5,6) 6
    新堆顶的弧信息: (3,5) 6
    
    Process finished with exit code 0
  • 相关阅读:
    A debugger is already attached
    鼠标指向GridView某列显示DIV浮动列表
    天气插件的替换
    ZPL打印中文信息
    「PowerBI」使用TabularEditor进行PowerBIDeskTop模型开发最佳实践
    「PowerBI」丢弃SSDT选择TabularEditor成为你的首选建模开发工具(下)
    「PowerBI」丢弃SSDT选择TabularEditor成为你的首选建模开发工具(中)
    「PowerBI」丢弃SSDT选择TabularEditor成为你的首选建模开发工具(上)
    「Azure」数据分析师有理由爱Azure之十-使用PowerShell自动化AzureAS
    「Azure」数据分析师有理由爱Azure之九-填坑-PowerBI Pro连接Azure AS模型
  • 原文地址:https://www.cnblogs.com/aimmiao/p/10123964.html
Copyright © 2020-2023  润新知