数据结构:关键路径求解

一、拓扑排序

1、基础概念

1)VOE(Activity On Vertex Network):这种用顶点表示活动,用弧表示活动时间的优先关系的有向图称为顶点表示活动的网,简称AOE网。
2)拓扑排序:所谓拓扑排序,就是将AOV网中所有顶点排成一个线性序列,该序列满足:若在AOV网中由顶点vi到顶点vj有一条路径,则在该线性序列中的顶点vi必定在顶点vj之前。
note that:如果一条有向网有环,则经由拓扑排序后,部分有向网中的顶点不会出现在拓扑序列中。the other word,可以通过拓扑排序 来判断一个有向网 是否有环。

2、拓扑排序步骤

1)在有向图中找一条无前驱的顶点 输出它;
2)从图中删除该顶点和所有以它为尾的弧;
3)重复1)和2),直至不存在无前驱的顶点;
4)若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在环,否则输出的顶点序列即为一个拓扑序列。

3、拓扑排序 伪代码
//有向图用 邻接表存储
//需要几个辅助数组
//indegree[]:存放各个顶点的入度
//topo[]:存放拓扑序列
//栈S:将度为0的顶点入栈
Status TopologicalSort(ALGraph G,int topo[]){
  FindIndegree(G,indegree); //找到每个顶点的入度:时间复杂度为O(e);
  InitStack(S);
  n = G.vexnum;
  for(i=0;i<n;++i){
    if(!indegree[i]){Push(S,i);}
  } //将入度为0的顶点入栈
  m=0; //记录拓扑序列中顶点个数
  while(!EmptyStack(S)){ //S非空时,执行while循环
    Pop(S,v); //将入度为0的顶点出栈
    topo[m] = v; //将顶点放入拓扑序列中
    ++m;
    p=G.vextices[v].firstarc; //删除以v为尾的弧,并修改另外一个顶点 的入度信息
    while(!p){
      j = p->adjvex; //顶点信息
      indegree[j] -= 1; //修改j的入度信息
      if(!indegree[j]){Push(S,j);} //如果j的入度信息为0,则将j入栈
      p = p->nextarc;
    }
  }
  if(m<n){return ERROR;}
  else{return OK;}
} 

总结:
1、拓扑排序算法:找各个顶点的入度信息时间复杂度为O(e),每个顶点入栈一次出栈一次,时间复杂度为O(n),修改邻接点入度信息时间复杂度为O(e),所以,拓扑排序算法的时间复杂度为O(n+e);

二、关键路径

1、相关概念

1)AOE(activity on edge):AOE网中,顶点代表事件,弧代表活动。AOE网常用于工程计划中,求解影响工程进度的关键路径。下图为一个AOE网,本节以此为例说明关键路径求解方法:

2)关键路径:要估算整项工程完成的最短时间,就是要找一条从源点到汇点的带权路径长度最长的路径,称为关键路径,关键路径上的活动称为关键活动。

2、关键路径求解思路

要求的关键路径 实际上 就是 满足下列条件的活动,即:活动的最早开始时间ve = 活动的最迟开始时间vl;

如果vl-ve > 0,则说明 可以留给活动一些缓冲时间,即使完成稍微晚一点也没关系,因此,不影响工程进度,这类活动,不是关键路径。

从上述的几个事实我们可以看出,要想求 关键路径,实际上就是求 ve=vl 的那些活动,接下来介绍 活动的 ve 和 vl 的求解方法。

活动ve 和 vl 的求解可以通过 求解 事件的ve和vl 来进行。

要求事件的ve和vl需要用到 拓扑排序。接下来具体求解事件的 最早发生时间 和 最迟发生时间。
1)事件vi的最早发生时间 ve(i):
进入事件vi的每个活动都结束后,vi才可以开始进行,因此,vi的最早发生时间 实际上是从 v0到vi的最长路径长度。
ve(0) = 0;
ve(i) = Max{ve(k) + wki};
2)事件vi的最迟发生时间vl(i):
事件vi的发生不得延误vi的每一后继事件的最迟发生时间。
假定vl(n-1) = ve(n-1);
vl(i) = Min{vl(j) - wij};

有了事件的最早 和 最迟 发生时间 以后,我们开始计算 活动(ai=<vi,vj>)的 最早开始时间 和 最晚开始时间:
1)ai的最早开始时间 = 事件vi的最早发生时间,即 ve(i);
2)ai的最迟开始时间 = 事件vj的最迟发生时间 - wij,即 vl(j) - wij;
当ai的 最早开始时间 = ai的最迟开始时间 时,说明ai是关键活动;

3、关键路径求解伪代码
//有向图 用 邻接表 存储
//topo[]:记录各个顶点的拓扑序列,根据这个序列求解 事件 最早/最迟 开始时间
Status CriticalPath(ALGraph G){
  //首先构建 有向图的拓扑序列
  if(!TopologicalOrder(G,topo)){return ERROR;} //构建拓扑序列
  n = G.vexnum; 
  for(i=0;i<n;++i){ve(i) = 0;} //初始化事件的最早开始时间
  for(i=0;i<n;++i){ //更新事件的最早开始时间
    v = topo[i];
    p = G.vextices[v].firstarc;
    while(!p){
      j = p->adjvex;
      if(ve[j] < ve[v] + p->weight){ve[j] = ve[v] + p->weight;}
      p = p->nextarc;
    }
   }
   for(i=0;i<n;++i){vl[i] = ve[n-1];} //初始化事件的最迟开始时间为 事件n-1的最迟开始时间
   for(i=n-1;i>=0;--i){ //更新事件的最迟开始时间
     v = topo[v];
     p = G.vextices[v].firstarc; 
     while(!p){
       j = p->adjvex;
       if(vl[v] > vl[j] - p->weight){vl[v] = vl[j] - p->weight;}
       p = p->nextarc;
     }
   }
   //求活动的最早 和 最迟开始时间
   for(i=0;i<n;++i){
     p = G.vextices[i].firstarc;
     while(!p){
       j = p->adjvex;
       e = ve[i];
       l = vl[j] - p->weight;
       if(e == l){ //满足条件,说明是关键活动
         cout<<G.vextices[i]<<G.vextices[j]
       }
       p = p->nextarc;
     }
   }
 } 

总结:
1、拓扑排序的时间复杂度为O(n+e),初始化事件最早/最迟开始时间的时间复杂度为O(n),更新事件最早/最迟开始时间的时间复杂度为O(n+e),寻找关键路径的时间复杂度为O(n+e),因此,算法总得时间复杂度为O(n+e);
2、在实际中,影响工程进程的因素很多,一个活动完工时间的变化,可能导致关键路径的变化;
3、在实际中,一个工程往往有多条关键路径,要想降低工程完成时间,则需要同时对多个 关键路径 进行提速;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sarah ฅʕ•̫͡•ʔฅ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值