-
关键路径
如果DAG图拓扑有序,那么此图可以转换成线性的先后关系,而不需要回退去访问数据。如果项目安排的事件拓扑有序,那么此项目中的各个事件的依存关系不可以解耦,不会形成连环套而致使项目无法开展。项目管理者除了合理安排各个项目事件,还需要清楚项目完成的最早事件。这个最早周期来自于项目图中的某个最长路径长度,这个最长路径长度也称作关键路径。 -
关键路径的算法
2.1 经典教材版
关键路径的算法一般有两类,一类算法来自经典严蔚敏的经典数据结构教材,在算法中规定了各个节点的最早发生时间和最晚发生时间,分别用ve[]和vl[]数组表示,根据节点的vel[]和vl[]可以相应得到各个活动(edge各边)的ee和el,如果ee和el相等,那么此边就为关键路径。具体算法不在此赘述,可以参考相关教材和博客。有兴趣可以参考此篇博文(如果侵权,请及时通知
https://blog.csdn.net/eternity_memory/article/details/1215427642.2 利用最长路径求关键路径
在解释关键路径算法之前,我们需要先了解最短路径的算法,如果图为DAG类型,那么求得拓扑有序之后,我们就可以按照拓扑储存,从前到后进行贪心遍历。拓扑有序可以让此贪心算法只关注其后来者,因为现有节点只会指向右方,不会回退(指向左方),所以可以线性进行贪心求解。
我们利用实际例子进行说明,
上图的其中之一的拓扑有序为A/B/C/D/E/F/G/H, 利用此拓扑有序,求解之前我们定义每个顶点含有两个属性:
a) dist[x],前置拓扑有序点到点x的最短距离
b) parent[x],顶点x的前置点,<parent[x],x> 为当前求得的最短距离
程序开始前,我们定义dist[x]=INT_MAX(程序中的最大正整数),然后按照拓扑有序,依次对各个顶点进行遍历,遍历过程中,及时更新dist[x]和parent[x]的值,确保当前距离最短。
如果以顶点A为起点,对顶点A进行遍历后,更新dist[B]=3, dist[C]=6, parent[B]=0, parent[C]=0, 按照此逻辑,逐个顶点进行更新,不断地对dist[]和parent[]进行relaxation.最后得到如下数组。
此数组代表从顶点A起,距离各个顶点的最短距离。
说了半天,那么如何利用此算法求解关键路径呢? 起始求解关键路径很简单,只要把边的权值取反(对应的负数),那么就可以利用此算法求解最短路径了。求解完成后,再对权值和取反。上面DAG可以在取反操作后,进行最短路径求解,此时的最短路径就是关键路径。
3. 关键路径的算法实现
3.1 首先对各边进行取反操作,求得权值的相反值
// Negate the weight of each edge
void Neg_Edge_Weight(ALGraph *G)
{
int i;
ArcNode *p;
for(i=0;i<G->vexnum;i++)
{
for(p=G->vertices[i].firstarc;p;p=p->nextarc)
{
*(p->info)=(-1)*(*(p->info));
}
}
return;
}
3.2 利用拓扑排序结果, ordering 数组,按照拓扑次序进行dist[]和parent[]数组更新
//ordering 数组算法,请参考上一篇博文
//dist[] 源点到各个顶点的最短距离
//parent[] 存储各边/弧度的tail
//Start 指定源点,起点
void DAG_Critical_Path(ALGraph G, int *dist, int *ordering, int *parent, int start)
{
int i;
int j;
int k;
int new_dist;
int distance;
ArcNode *p;
for(i=0;i<G.vexnum;i++)
{
*(dist+i)=INT_MAX;
*(parent+i)=-1;
}
*(dist+start)=0;
for(i=0;i<G.vexnum;i++)
{
j=*(ordering+i); //Get Topological order number
if(dist[j]!=INT_MAX)
{
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
distance=*(p->info);
new_dist=dist[j]+distance;
if(parent[k]==-1)
{
dist[k]=new_dist;
parent[k]=j;
}
else
{
if(new_dist<dist[k])
{
dist[k] = new_dist;
parent[k] = j;
}
}
}
}
}
}
3.3 利用递归,打印出关键路径
void Display_Critical_Path(ALGraph G, int start, int end, int *parent)
{
int i;
int k;
if(end!=start)
{
k = *(parent + end);
Display_Critical_Path(G,start,k,parent);
}
printf("-%c-\n",G.vertices[end].data);
}
- 总结
此算法充分利用topologicalsort求出有序的顶点,对每个顶点只不需要“瞻前”,无需“顾后”,然后利用贪心算法,不断更新每个顶点至指定源点的最小距离,最后求得每个顶点至源点的最小距离。关键路径算法,对每个权值先进行取反,然后利用最短距离算法,求得源点-终点的关键路径。
-参考文献
1.《数据结构》清华大学,严蔚敏
2. Video,shortest/longest path on DAG by William Fiset