第六章 图——拓扑排序与关键路径,最短路径

数据结构总结

拓扑排序

 1 拓扑排序(在没有回路的情况下实现)

概念

由某个元素集合上的一个偏序得到该集合上的一个全序,该操作称为拓扑排序。

应用: 检查有向图是否存在回路。

没有回路,有先后关系,可表示工程施工计划,除协助制定施工序列外,还可判断计划是否有问题(工程能否顺利开展)

 AOV-网:顶点表示活动,弧表示活动间先后(依赖)关系的有向图。

 

2 实现拓扑排序

算法原理

g比较特殊,因为它有前驱 

代码实现

 

 

CountInDegree(G, indegree)
{
    for(int i=0; i<G.vexnum; ++i)
        for(p=G.vertices[i].firstarc; p!=NULL; p=p->nextarc)
            ++indegree[p->adjvex];
}//找出每个点的入度数
外层循环遍历图G的所有顶点,从0到G.vexnum-1。
内层循环遍历当前顶点i的所有邻接边,从G.vertices[i].firstarc开始,直到p为NULL。
对于每条邻接边p,将p->adjvex对应的入度数组indegree的值加1。
Status TopologicalSort(ALGraph G)
{
    CountInDegree(G, indegree);//找出每个点的入度数
    InitStack(S);
    for(i=0; i<G.vexnum; ++i)
        if(!indegree[i]) Push(S,i);//入度为0的顶点入栈
    count = 0;  //用以对输出的顶点进行计数
    while (!StackEmpty(S))
    {
        Pop(S,i);
        OutPutElem (G.vertices[i].data);
        ++count;
        for(p=G.vertices[i].firstarc; p!=NULL; p=p->nextarc)
        {
            k=p->adjvex;
            - -indegree[k];
            if(!indegree[k]) Push(S,w);//新产生的入度为0的顶点入栈
        }
    }
    if(count<G.vexnum) return ERROR;//图中有回路
    else return OK;//图中没有回路
}//T(n)=O(n+e)

 关键路径

概念

 

最长路径 

时间

补充说明

求事件最早发生时间和关键路径的快速方法

算法实现 

所有事件状态的最早激活时间

与拓扑排序基本相同,多了一个各点ve初始化零及更新后继节点最早激活时间的步骤;为方便后面求各顶点最晚激活时间还要用栈T记录拓扑逆序
Status GetVEAndRvsTopOrd(ALGraph G,Stack  &T){
//求各顶点最早到达时间计入全局数组ve,T按拓扑序存储顶点
   FindInDegree(G,indegree);
   InitStack(S);  for(i=0;i<G.vexnum;++i) if(!indegree[i]) Push(S,i);
   ve[0..G.vexnum-1]=0;
   InitStack(T);  //用来求拓扑逆序
  count=0;  //对输出的顶点进行计数
  while (!StackEmpty(S)){//出栈S、入栈T、”删除”、更新
      Pop(S,j); ++count;
      Push(T,j);
     for(p=G.vertices[j].firstarc; p;  p=p->nextarc){
         k=p->adjvex;  --indegree(k); 
        if(indegree[k]= =0) Push(S, k);
        if( ve[j]+*(p->info) > ve[k] )  ve[k] = ve[j]+*(p->info);//info存权
    }//for
 }//for
  if(count<G.vexnum)return ERROR; else return OK;}

关键路径 

​
关键路径: 调用子函数求ve,按拓扑逆序求vl,求ee/el并输出关键否
Status CriticalPath(ALGraph G){
  InitStack(T);  // 初始化栈T
  if(! GetVEAndRvsTopOrd(G,T) )  return ERROR;  // 获取顶点的最早激活时间和拓扑排序
  vl [0..G.vexnum-1]=ve[G.vexnum-1];  // 将最晚激活时间初始化为最后一个顶点的最早激活时间
  while(!StackEmpty(T)){
     Pop(T,j);  // 弹出栈顶元素j
      for(p=G.vertices[j].firstarc; p!=NULL; p=p->nextarc){
          k=p->adjvex;    dut=*(p->info);  // 获取邻接顶点k和持续时间dut
          if( vl[k]-dut < vl[j] ) vl[j]= vl[k]-dut;  // 更新最晚激活时间
      }
   }
   for(j=0;j<G.vexnum;++j)
       for(p=G.vertices[j].firstarc;p;p=p->nextarc){
           k=p->adjvex; dut=*(p->info);
           ee=ve[j];  el=vl[k]-dut//活动<j,k>的最早/迟开始时间
          if(ee==el) Print (j,k,dut,ee,el, √ );  // 如果最早和最晚时间相等,则输出为关键活动
          else       Print (j,k,dut,ee,el, × );  // 否则输出为非关键活动
}}

​

所有事件状态的最晚到达时间

所有活动的最早最晚激活时间(e和l通过ve和vl求得)

最短路径

求解最短路的Dijkstra算法 

Dijkstra算法思想

 没有同时经过v1,v2  

Dijstra算法实现

代码原理

没有直接相连为无穷值 

 代码

原理理解 

初始化从u 

判断dist, c的最小,从c

 判断dist,除了c,a最小,从a,更新dist

 

  判断dist,除了c,a,d小,从d,更新dist 

 

finished[k]标记k号顶点是否处理结束
D[k]存储源到顶点k的当前最短路长度
P[v] 记录源到v的当前最短路径
finished[v0]=TRUE;
D[v0]=0;
void ShortestPath_FLOYD(MGraph G, int v0,PathArray &P,  DistancArray &D){
(1)源标记Finished,初始化源到V-S中各点直接距离
for(v=0; v<G.vexnum; ++v)
{
    if(v!=v0)
    {
        finished[v]=FALSE;
        D[v]=G.arcs[v0][v];
    }
    if(D[v]<INFINITY)
    {
        P[v] = v0 -> v;//P[v]被赋值为从v0到v的路径。
    }
}
(2)未Finished的点中找距离最小点,标记其Finished
for(i=1; i<=G.vexnum-1; ++i)
{
    min=INFINITY;
    for(w=0; w<G.vexnum; ++w)      
     if(!finished[w]&&D[w]<min)
        {
            v=w;
            min=D[w];
        }
    finished[v]=TRUE;
  (3)根据新finished顶点更新源到其他点的距离
    for(w=0; w<G.vexnum; ++w)
    {
        if( !finished[w] && (min+G.arcs[v][w])< D[w] )
        {
            D[w]= min+G.arcs[v][w];
            P[w] =strcat(P[v], “->w”);
        }
    }
}
(3)根据新finished顶点更新源到其他点的距离

}

复杂度   T(n)=O(G.vexnum2 ) 顶点个数的平方

 Floyd算法

Floyd算法原理

 Step0:计算从i出发、跳点为空、直接到j的最短路. 此时最短路对应的距离记入矩阵D(0)[i][j]

Step1:可选跳点为{1}时,i到j的路径分两种情况, 或者不经过1,此时最短距离为D(0)[i][j], 或者经过1, 此时最短为D(0)[i][1]+D(0)[1][j] D(1)[i][j]=min{D(0)[i][j], D(0)[i][1]+D(0)[1][j]}

加入a,更新路径表 

Step2:可选跳点为{1,2}时i到j的路径分两种情况, 或者不经过2,此时最短距离为D(1)[i][j], 或者经过2, 此时最短为D(1)[i][2]+D(1)[2][j] D(2)[i][j]=min{D(1)[i][j], D(1)[i][2]+D(1)[2][j]}

加入b,更新路径表 

Step k:可选跳点为{1,2,…,k}(下标不超过k)时, 或者不经过k,此时最短距离为D(k-1)[i][j], 或者经过k, 此时最短为D(k-1)[i][k]+D(k-1)[k][j] D(k)[i][j]=min{D(k-1)[i][j], D(k-1)[i][k]+D(k-1)[k][j]}

Step n:可选跳点为{1,2,…,n}时,意味着对中间经过的顶点不加任何限制,此时所得最短路就是全局最短路 D(n)[i][j]=       min{D(n-1)[i][j],D(n-1)[i][n]+D(n-1)[n][j] }

总结

D(0)[i][j]=G.arcs[i][j]

D(k)[i][j]=min{D(k-1)[i][j],  D(k-1)[i][k]+D(k-1)[k][j] }

Floyd算法实现 

void GetShortestPath_FLOYD (MGraph G,
          PathMatrix &P, DistancMatrix &D){
 //初始化D(0)[][]与P(0)[][]
 for(i=1; i<=G.vexnum; ++i){
   for(j=1; j<=G.vexnum; ++j){
      D[i][j] = G.arcs[i][j];
      if(D[i][j] != INFINITY){
         P[i][j] = i + “->” + j;
      }
   } 
 }
  //迭代求Dk[][]与Pk[][],k=1 ->G.vexnum
  for(k=1; k<=G.vexnum; ++k){
     for(i=1; i<=G.vexnum; ++i)
        for(j=1; j<=G.vexnum; ++j)
           if(D[i][k]+D[k][j] < D[i][j]){
               D[i][j] = D[i][k]+ D[k][j];
               P[i][j] = MyStrCat(P[i][k],P[k][j]);
           }
}

总结与推广

1.

2.动态规划法

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值