7、最小生成树,克鲁斯卡尔(Kruskal)算法

  1)算法的基本思想:

前面我们学习过Prim算法,他是一种以某个节点出发,按权值递增的次序选择合适的边来构造最小生成树的方法,他的时间复杂度为O(n2),与顶点有关,而与边无边,所以适合求边稠密的图的生成树。

算法构造一颗最小生成树的过程如下(母图基于Prim算法部分的无向图):

2)算法的描述:

在图中任取一个顶点K作为开始点,令U={k},W=V-U,其中V为图中所有顶点集,然后找一个顶点在U中,另一个顶点在W中的边中最短的一条,找到后,将该边作为最小生成树的树边保存起来,并将该边顶点全部加入U集合中,并从W中删去这些顶点,然后重新调整U中顶点到W中顶点的距离, 使之保持最小,再重复此过程,直到W为空集止。

假设G=(V,E)是一个具有n个顶点的带权无向连通图,T= (U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,则构造最小生成树的过程如下:

(1) 置U的初值等于V,TE的初值为空集;

(2) 按权值从小到大的顺序依次选取图G中的边,若选取的边未使生成树T形成回路,则加入TE;若选取的边使生成树T形成回路,则将其舍弃。循环执行(2),直到TE中包含(n-1)条边为止。

3)C语言描述:

为实现克鲁斯卡尔算法需要设置一维辅助数组E,按权值从小到大的顺序存放图的边,数组的下标取值从0到e-1(e为图G边的数目)。 

假设数组E存放图G中的所有边,且边已按权值从小到大的顺序排列。n为图G的顶点个数,e为图G的边数。克鲁斯卡尔算法如下:

#define MAXE <最大边数>

#define MAXV <最大顶点数>

typedef struct

int vex1;                     //边的起始顶点

int vex2;                      //边的终止顶点

int weight;                    //边的权值

}Edge;

 

Voidkruskal(Edge E[],int n,int e)

{ inti,j,m1,m2,sn1,sn2,k;

  int vset[MAXV];

  for(i=0;i<n;i++)        //初始化辅助数组

    vset[i]=i;

  k=1;        //表示当前构造最小生成树的第k条边,初值为1

 j=0;                   //E中边的下标,初值为0

  while(k<e)            //生成的边数小于e时继续循环

  { ml=E[j].vex1;m2=E[j].vex2;//取一条边的两个邻接点

     sn1=vset[m1];sn2=vset[m2];                             

       //分别得到两个顶点所属的集合编号

     if(sn1!=sn2) 

        //两顶点分属于不同的集合,该边是最小生成树的一条边

{ printf(“(m1,m2):%d\n”,E[j].weight);

       k++;                //生成边数增l

       for(i=0;i<n;i++)    //两个集合统一编号

         if (vset[i]=sn2)      //集合编号为sn2的改为sn1

           vset[i]=sn1;

     }

     j++;                  //扫描下一条边

  }

}

4)C语言完整实现

#defineMAXE 11

#defineMAXV 10

#include"stdio.h"

typedefstruct

intvex1;                     //边的起始顶点

intvex2;                      //边的终止顶点

intweight;                    //边的权值

}Edge;

 

void kruskal(Edge E[],int n,int e)

{ int i,j,m1,m2,sn1,sn2,k;

  intvset[MAXV];

 for(i=1;i<=n;i++)        //初始化辅助数组

   vset[i]=i;

  k=1;        //表示当前构造最小生成树的第k条边,初值为1

  j=0;                   //E中边的下标,初值为0

   while(k < e)            //生成的边数小于e时继续循环

   { 

       m1=E[j].vex1;

       m2=E[j].vex2;//取一条边的两个邻接点

       sn1=vset[m1];

       sn2=vset[m2];                             

       //分别得到两个顶点所属的集合编号

     if(sn1!=sn2) 

        //两顶点分属于不同的集合,该边是最小生成树的一条边

         { 

                      printf("(v%d,v%d):%d\n",m1,m2,E[j].weight);

              k++;                //生成边数增l

                     if(k>=6)  break;

             for(i=1;i<=n;i++)    //两个集合统一编号

                if(vset[i]==sn2)  //集合编号为sn2的改为sn1

                   vset[i]=sn1;

         }//if

     j++;                  //扫描下一条边

   }//while

}//kruskal

 

intmain()

{

EdgeE[MAXE];

intnume,numn;

//printf("输入边数和顶数:\n");

//scanf("%d%d",&nume,&numn);

nume=10;

numn=6;

printf("请输入各边及对应的的权值(起始顶点 终止顶点 权值)\n");

/*

for(inti=0;i<nume;i++)

       scanf("%d%d%d",E[i].vex1,E[i].vex2,E[i].weight);

*/

//在这里对输入的数据进行排序,达到假设的要求,即:假设数组E存放图G中的

//所有边,且边已按权值从小到大的顺序排列

E[9].vex1=1;

E[9].vex2=2;

E[9].weight=6;

E[0].vex1=1;

E[0].vex2=3;

E[0].weight=1;

E[4].vex1=1;

E[4].vex2=4;

E[4].weight=5;

E[6].vex1=2;

E[6].vex2=3;

E[6].weight=5;

E[2].vex1=2;

E[2].vex2=5;

E[2].weight=3;

E[8].vex1=1;

E[8].vex2=2;

E[8].weight=6;

E[5].vex1=3;

E[5].vex2=4;

E[5].weight=5;

E[7].vex1=3;

E[7].vex2=5;

E[7].weight=6;

E[3].vex1=3;

E[3].vex2=6;

E[3].weight=4;

E[1].vex1=4;

E[1].vex2=6;

E[1].weight=2;

kruskal(E,numn,nume);

}

 5)以上我们只是作抛砖引玉。比如节点的数量,边的数量,都可以根据实际情况来增减。

最小生成树在实际生活中主要用来解决生活中的优化问题。比如在几个大城市间修建路,如何修才能省时省力等。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值