图的应用:最小生成树

一、应用场景

n个城市之间想要建立一个通信线路,有很多种方法,把城市看作是顶点,城市之间的线路看作是路径,则n个城市之间构建的通信线路 可以看成是一个 连通网,为了节省费用,势必是想要找到其中一个最小的 连通网的,这种找 最小 “连通网(带权路径和)”的问题,我们可以用 最小生成树算法 来解决。

最小生成树:各边的代价之和最小的那棵生成树称为该连通网的最小代价生成树,简称最小生成树;

二、最小生成树算法

1、普里姆算法:给定一个起始顶点,求最小生成树(加点法)

普里姆算法基于一个这样的思想,即:如果<u,v>是一条具有最小权值的边,则这条边一定包含在最小生成树中;

算法思想:假设U中现有u0一个顶点,V-U中为V中其它顶点,TE为所求的最小边集合:
step1:在所有u belong to U,v belong to V-U中,找到一条权值最小的边<u,v>,并将该边的顶点v也并入集合U中,边<u,v>并入集合TE中;
step2:重复步骤1,直到U=V为止;

以一个顶点为开始,求最小生成树,如下图所示,为利用普里姆算法求 最小生成树 的图示:

普里姆算法伪代码:

//以 邻接矩阵 表示图G
struct{  //存储最短边
  VerTexType adjvex;
  ArcType lowcost;
}closedge[MVNum];
void MiniSpanTree_Prim(AMGraph G,VerTexType u){
  k = LocateVex(G,u); //返回顶点u在G中的下标
  for(i=0;i<G.vexnum;++i){ //初始化各个顶点的 最短边
    if(i != k){closedge[i] = {u,G.arcs[k][i]};}
  }
  closedge[k].lowcost = 0;
  for(i=1;i<G.vexnum;++i){
    m = MinArc(closedge); //找出最短边 对应的顶点(V集合中的顶点) 下标;
    u~0~ = closedge[m].adjvex;
    v~0~ = G.vex[m];
    cout<<u~0~<<v~0~; //将最短边输出
    closedge[m].lowcost = 0;
    for(i=0;i<G.vexnum;++i){
      if(G.arcs[m][i] < closedge[i].lowcost){
        closedge[i].lowcost = G.arcs[m][i]; //更新各个顶点的最短边
        closedge[i].adjvex = G.vex[m]; //更新其邻接点信息
      }
    }
  } 

总结:
1、普里姆算法包含一个2层for循环,因此,其时间复杂度为O(n2);
2、普里姆算法运算过程中,只涉及到找顶点的操作,而没有涉及边,因此,普里姆算法的时间复杂度与边数无关,适用于 求 稠密网的最小生成树;

2、克鲁斯卡尔算法(加边法)

通过搜集 最小边的 方式 构造 最小生成树;

克鲁斯卡尔算法 具体步骤如下:
step1:假设连通网N=(V,E),将N中的边按权值从小到大的顺序排列;
step2:选取最小边,如果最小边的两个顶点不属于同一个连通分量,则将这条边纳入TE集合中,否则舍去,寻找下一条最小边。在最开始时,连通网中各个顶点自成一个连通分量;
step3:重复step2,直到所有的顶点都出现在最小生成树中;

克鲁斯卡尔算法 构成最小生成树 的过程(原连通图 同 普里姆算法中的连通图):

克鲁斯卡尔算法伪代码:

struct{
  VerTexType head; //边的头结点
  VerTexType tail; //边的尾结点
  ArcType lowcost; //边的权重
}Edge[arcnum];
void MiniSpanTree_Kruskal(AMGraph G){
//初始化所有顶点所处连通分量编号 为自己
  for(i=0;i<G.vexnum;++i){
    Vexset[i] = i;
  }
  sort(Edge); //将边按从小到大的顺序从新排序
  for(i=0;i<G.arcnum;++i){
    u = LocateVex(G,Edge[i].head); //找出头结点在图G的下标
    v = LocateVex(G,Edge[i].tail); //找出尾结点下标
    us = Vexset[u]; //head所属连通分量
    vs = Vexset[v]; //tail所属连通分量
    if(us != vs){
      cout<<G.vex[us]<<G.vex[vs]; //输出最短边
      for(i=0;i<G.vexnum;++i){
        if(Vexset[i] == vs){Vexset[i] = us;} //将一个连通分量的编号统一
      }
    }
  }
}

总结:
1、克鲁斯卡尔算法中 sort(Edge) ,如果用堆排序的话,其时间复杂度为O(elog2e)。而for循环中最费劲的部分是合并两个不同的连通分量,只要采取合适的数据结构,可以证明其执行时间为O(log2e),因此,整个算法下来,其时间复杂度为O(elog2e)。普里姆算法的时间复杂度为:O(n2);
2、克鲁斯卡尔算法 是对边进行操作,因此,可以用于 稀疏网的 最小生成树 的生成;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sarah ฅʕ•̫͡•ʔฅ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值