zoukankan      html  css  js  c++  java
  • 最小生成树

    最小生成树

      最小生成树就是将一个图中的若干边去掉,使之成为一棵树,这棵树的各条边的权值之和最小。

      构造最小生成树有两个典型的算法——Prim算法和Kruskal算法。下面就分别介绍这两个算法。两个算法都是开始将各个顶点孤立,然后依次选取若干边将顶点连接起来构成一棵树。

    Prim算法

      算法的流程大致如下:

    (1)选取任意顶点V0,将其放入已访问集合V(未访问集合为U)。

    (2)遍历已访问集合中所有顶点,选取到未访问集合中的顶点距离最小的点加入访问集合。

    (3)当所有的顶点都在已访问集合中时,算法结束。

    因此,我们定义lowcost数组记录V到U中的i顶点中的距离,定义closeset数组记录i顶点是由哪一个顶点延伸出来的。以此来构造最后的生成树。

     1 #include<cstdio>
     2 #define MAXSZIE 100
     3 typedef struct Vertex
     4 {
     5     int num;
     6 } Vertex;
     7 typedef struct MGraph
     8 {
     9     int n,e;
    10     int edges[MAXSZIE][MAXSZIE];
    11     Vertex vex[MAXSZIE];
    12 } MGraph;
    13 int BTree[MAXSZIE][MAXSZIE];
    14 MGraph create()
    15 {
    16     int n,e;
    17     int a,b,val;
    18     MGraph g;
    19     scanf("%d%d",&n,&e);
    20     g.n=n;
    21     g.e=e;
    22     for(int i=0; i<n; i++)
    23         g.vex[i].num=i;
    24     for(int i=0; i<e; i++)
    25         for(int j=0; j<e; j++)
    26             g.edges[i][j]=99999;
    27     for(int i=0; i<e; i++)
    28     {
    29         scanf("%d%d%d",&a,&b,&val);
    30         g.edges[a][b]=val;
    31         g.edges[b][a]=val;
    32     }
    33     return g;
    34 }
    35 void prim(MGraph g,int v0)
    36 {
    37     int closeset[MAXSZIE];
    38     int lowcost[MAXSZIE];   //生成树到其余节点权值
    39     int father[MAXSZIE];
    40     int bset[MAXSZIE];
    41     for(int i=0; i<g.n; i++)
    42     {
    43         bset[i]=0;
    44         closeset[i]=0;
    45         lowcost[i]=g.edges[v0][i];
    46     }
    47     bset[v0]=1;
    48     for(int i=1; i<g.n; ++i)
    49     {
    50         int minval=9999;
    51         int k;
    52         for(int j=1; j<g.n; ++j)
    53         {
    54             if(bset[j]==0&&lowcost[j]<=minval)
    55             {
    56                 minval=lowcost[j];
    57                 k=j;
    58             }
    59         }
    60         father[k]=closeset[k];
    61         BTree[k][father[k]]=minval;
    62         BTree[father[k]][k]=minval;
    63         bset[k]=1;
    64         for(int j=1; j<g.n; ++j)
    65         {
    66             if(bset[j]==0&&g.edges[k][j]<lowcost[j])
    67             {
    68                 lowcost[j]=g.edges[k][j];
    69                 closeset[j]=k;
    70             }
    71 
    72         }
    73     }
    74 }
    75 int main()
    76 {
    77     MGraph g = create();
    78     prim(g,0);
    79     for(int i=0; i<g.n; i++)
    80     {
    81         for(int j=0; j<g.n; j++)
    82             printf("%d	",BTree[i][j]);
    83         printf("
    ");
    84     }
    85     return 0;
    86 }

      如上述代码所示,我们使用邻接矩阵表示图,不连通的顶点之前的权值为无穷大。

      依次看所有的节点,每次选取最小的边进行扩展,所以就是每次选择一个顶点加入到最小生成树中,当我们将所有顶点遍历一遍(最外层循环)的时候,算法结束。

      每次扩展节点,我们选取到未扩展集合权值最小的边,也就是选取lowcost中值最小的,记录其顶点坐标k。延伸出k的closeset的值就是k的父节点。

      每扩展完一次,就要对lowcost数组和closeset数组进行更新,对应代码的66~70行,也就是说,如果新延伸的节点k到未访问集合的距离如果更短,那么未访问集合中下一个延伸的节点肯定是由k延伸出去的,否则是由其他点延伸出去的。这个显然啊,因为下一次也是从V的邻接边中找最小的,你从k出发,到未访问的节点小于你之前的lowcost数组中的值,那么下一次延伸的节点必然是从k出去的(从其他节点到未访问集合的距离已经被更新)。

      说的有点乱,举个栗子。

                            

      一开始,我们将0标记为已访问。所有的节点一开始默认从0延伸出去。此时,lowcost[1]=5,下一次,找到了2节点(就是上边所说的k),2加入到集合中,此时,从2到1的距离为3,比lowcost[1]=5要小,所以,1肯定不能由0延伸出去,肯能是从2延伸,那么就要更新1的lowcost值和closeset值,当我们扩展1的时候,2通过closeset就直接找到了其父节点。

      对于Prim算法的时间复杂度,很明显是O(n2)的。

    Kruskal算法

      算法的大致流程如下:

    (1)将所有的边按从小到大排序。

    (2)从小到大依次选择边构成树,注意不能成环。

    (3)所有的边遍历一遍后,算法结束

    将节点依次加入集合中构成树,需要用到并查集

      1 #include<cstdio>
      2 #define MAXSZIE 100
      3 typedef struct Vertex
      4 {
      5     int num;
      6 } Vertex;
      7 typedef struct MGraph
      8 {
      9     int n,e;
     10     int edges[MAXSZIE][MAXSZIE];
     11     Vertex vex[MAXSZIE];
     12 } MGraph;
     13 int BTree[MAXSZIE][MAXSZIE];
     14 typedef struct Road
     15 {
     16     int a,b;
     17     int weight;
     18 } Road;
     19 Road r[MAXSZIE];
     20 int count=0;
     21 MGraph create()
     22 {
     23 
     24     int n,e;
     25     int a,b,val;
     26     MGraph g;
     27     scanf("%d%d",&n,&e);
     28     g.n=n;
     29     g.e=e;
     30     for(int i=0; i<n; i++)
     31         g.vex[i].num=i;
     32     for(int i=0; i<e; i++)
     33         for(int j=0; j<e; j++)
     34             g.edges[i][j]=99999;
     35     for(int i=0; i<e; i++)
     36     {
     37         scanf("%d%d%d",&a,&b,&val);
     38         g.edges[a][b]=val;
     39         g.edges[b][a]=val;
     40         r[count].a=a;
     41         r[count].b=b;
     42         r[count].weight=val;
     43         count++;
     44     }
     45     return g;
     46 }
     47 int father[MAXSZIE];
     48 void init(MGraph g)
     49 {
     50     for(int i=0; i<g.n; ++i)
     51         father[i]=i;
     52 }
     53 int getfather(int x)
     54 {
     55     if(x!=father[x])
     56         father[x]=getfather(father[x]);
     57     return father[x];
     58 }
     59 void unio(int x,int y)
     60 {
     61     int m,n;
     62     m=getfather(x);
     63     n=getfather(y);
     64     if(m!=n)
     65     {
     66         father[m]=n;
     67         BTree[x][y]=1;
     68     }
     69 }
     70 void getSort(MGraph g)
     71 {
     72     Road tempr;
     73     for(int i=0; i<g.e-1; ++i)
     74     {
     75         for(int j=i+1; j<g.e; ++j)
     76         {
     77             if(r[i].weight>r[j].weight)
     78             {
     79                 tempr=r[j];
     80                 r[j]=r[i];
     81                 r[i]=tempr;
     82             }
     83         }
     84     }
     85 }
     86 void Kruskal(MGraph g)
     87 {
     88     getSort(g);
     89     int x,y;
     90     for(int i=0; i<g.e; i++)
     91     {
     92         unio(r[i].a,r[i].b);
     93     }
     94 }
     95 int main()
     96 {
     97     MGraph g = create();
     98     init(g);
     99     Kruskal(g);
    100     for(int i=0; i<g.n; ++i)
    101     {
    102         for(int j=0; j<g.n; ++j)
    103         {
    104             printf("%d	",BTree[i][j]);
    105         }
    106         printf("
    ");
    107     }
    108     return 0;
    109 }

      算法很简单,只要对边进行排序,然后依次合并就可以了,这里就不举栗子了。所以算法的时间复杂度与排序算法的时间复杂度相同。

      

  • 相关阅读:
    KNN算法--物以类聚,人以群分
    朴素贝叶斯算法原理
    17.Letter Combinations of a Phone Number
    103.Binary Tree Zigzag Level Order Traversal
    65、使用互斥锁唤醒指定线程
    64、线程之间的通信
    63、使用Timer类来实现定时任务
    62、单例模式
    61.volatile关键字
    60、死锁
  • 原文地址:https://www.cnblogs.com/wktwj/p/4901519.html
Copyright © 2011-2022 走看看