DAG 图关键路径算法

  1. 关键路径
    如果DAG图拓扑有序,那么此图可以转换成线性的先后关系,而不需要回退去访问数据。如果项目安排的事件拓扑有序,那么此项目中的各个事件的依存关系不可以解耦,不会形成连环套而致使项目无法开展。项目管理者除了合理安排各个项目事件,还需要清楚项目完成的最早事件。这个最早周期来自于项目图中的某个最长路径长度,这个最长路径长度也称作关键路径。

  2. 关键路径的算法
    2.1 经典教材版
    关键路径的算法一般有两类,一类算法来自经典严蔚敏的经典数据结构教材,在算法中规定了各个节点的最早发生时间和最晚发生时间,分别用ve[]和vl[]数组表示,根据节点的vel[]和vl[]可以相应得到各个活动(edge各边)的ee和el,如果ee和el相等,那么此边就为关键路径。具体算法不在此赘述,可以参考相关教材和博客。有兴趣可以参考此篇博文(如果侵权,请及时通知
    https://blog.csdn.net/eternity_memory/article/details/121542764

    2.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);
  
}

  1. 总结
    此算法充分利用topologicalsort求出有序的顶点,对每个顶点只不需要“瞻前”,无需“顾后”,然后利用贪心算法,不断更新每个顶点至指定源点的最小距离,最后求得每个顶点至源点的最小距离。关键路径算法,对每个权值先进行取反,然后利用最短距离算法,求得源点-终点的关键路径。

-参考文献
1.《数据结构》清华大学,严蔚敏
2. Video,shortest/longest path on DAG by William Fiset

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值