学习笔记:图的拓扑排序和关键路径

图的拓扑排序和关键路径

  • 图的拓扑排序
  • 图的关键路径

图的拓扑排序

假设以有向图表示一个工程的施工图或程序的数据流图,每个顶点代表一个活动,弧表示活动i必须先于活动j进行,称为AOV-网(Activity On Vertex)

图中不允许出现回路。

  • 检查有向图中是否存在回路的方法之一,时对有向图进行拓扑排序。
  • 还可以使用DFS

何谓拓扑排序?

对有向图进行如下操作:

按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意次序关系,由此所得顶点的线性序列称之为拓扑有序序列。显然对于有回路的有向图得不到拓扑有序序列。

学习笔记:图的拓扑排序和关键路径_第1张图片

可求得拓扑有序序列:

A B C D 或 A C B D

如何进行拓扑排序?

  1. 从有向图选取一个没有前驱的顶点,输出之;
  2. 从有向图中删去此顶点以及所有以它为尾的弧;

重复上述两步,直至空图,或者图不空但找不到无前驱的顶点为止。

拓扑排序的算法实现:

没有前驱的顶点==入度为零的顶点

删除顶点以及它的出弧==弧头顶点的入度-1

学习笔记:图的拓扑排序和关键路径_第2张图片

各顶点入度的函数:

void FindID(AdjListb G,int indegree[MAX_VERTEX_NUM])
{
	int i;
    ArcNode *p;
    for(i=0;i<G.vexnum;i++)
        indegree[i]=0;
    for(i=0;i<G.vexnum;i++){
        p=G.vertexes[i].firstarc;
        while(p!=NULL){
            indegree[p->adjvex]++;
            p=p->nextarc;
        }
    }
}

将入度为0的结点入队

算法实现:

int TopoSort(AdjList G)
{
	Queue Q;
	int indegree[MAX_VERTEX_NUM];
	int i,count,k;
	ArcNode *p;
	FindID(G,indegree);
	InitStack(S);
	for(i=0;i<G.vexnum;i++)
		if(indegree[i]==0)
			EnterQueue(Q,i);
	count=0;
	while(!StackEmpty(S))
	{
		DeleteQueue(Q,i);
		printf("%c",G.vertex[i].data);
		count++;
		p=G.vertexes[i].firstarc;
		while(p!=NULL)
		{
			k=p->adjvex;
			indegree[k]--;
			if(indegree[k]=0)
				EnterQueue(S,k);
			p=p->nextarc;
		}
	}
	if(count<G.vexnum)
		return Error;
	else
		return OK;
}

图的关键路径

AOE网是指在有向网中,如果用顶点表示事件用有向边表示活动,边上的权值表示活动持续的时间,则称这样的有向网为弧表示活动的网。

对仅有一个开始点和一个完成点的工程,可用AOE-网来表示,它可用来估算工程的完成时间

学习笔记:图的拓扑排序和关键路径_第3张图片

  • 图中仅有一个入度为0的顶点称为源点,表示工程的开始;同时,也仅有一个出度为0的顶点称为汇点,表示工程的结束

  • 只有在进入某一顶点的各有向边所代表的活动均已完成,该顶点所代表的事件才能发生。

  • 只有在某顶点所代表的事件发生后,从该顶点发出的所有有向边所代表的活动才能开始。

  • 整个工程完成的时间为:

    从有向图的源点汇点的最路径,即关键路径

  • 关键活动持续时间的总和(关键路径的长度) 就是完成整个工程的最短工期。

  • 如何求关键活动呢?

相关概念: ve(j):v是顶点,即事件,e是early,即最早,j是顶点vj

ve(j)=从源点到顶点j的最长路径长度。即事件vj的最早发生时间。

ve(源点)=0;

ve(j)=Max{ve(k)+dut()}

学习笔记:图的拓扑排序和关键路径_第4张图片

ve(j)的值可从源点开始按拓扑顺序向汇点递推

相关概念:vl(k):v是顶点,即事件,l是late,即最迟,k是顶点vk

vl(k)=从顶点k到汇点的最短路径长度。即事件vj的最迟发生时间。

vl(汇点)=ve(汇点);

vl(k)=Min{vl(j)-dut()}

学习笔记:图的拓扑排序和关键路径_第5张图片

vl(j)的值可从汇点开始按逆拓扑顺序向源点递推。

活动的最早开始时间:

ee(i)=ve(j)

即:假设第i,则ee(i)为第i项活动(弧)的最早开始时间

活动的最晚开始时间:

el(i)=vl(k)-dut()

即:el(i)为第i项活动(弧)的最迟开始时间

还是上面的图:

学习笔记:图的拓扑排序和关键路径_第6张图片

关键活动就是ee(ai)=el(aj)的活动。

学习笔记:图的拓扑排序和关键路径_第7张图片

关键路径的求解步骤:

  1. 按拓扑顺序求出每个事件的最早发生时间;
  2. 按逆拓扑序列求出每个事件的最迟发生时间;
  3. 计算每个活动的最早开始时间和最晚发生时间;
  4. 找出关键活动,即ee(ai)=el(ai)的活动。

改造拓扑排序算法:

  1. 求出各顶点的入度,并将入度为0的顶点入队;
  2. 将各顶点的最早发生时间ve(i)初始化为0;
  3. 只要队列不空,重复以下步骤
    • ​ 将队头顶点j出队,同时压入栈T(逆拓扑序列)
    • ​ 将队头j的每一个邻接点k的入度减1,如果顶点k的入度减为0,则将顶点k入队,
    • ​ 再根据顶点j的最早发生时间ve(j)和弧的权值,更新顶点k的最早发生时间ve(k)。

关键路径算法步骤:

  1. 调用修改后的拓扑排序算法,求出每个事件的最早发生时间和逆拓扑序列栈T;
  2. 将各顶点的vl(i)初始化为汇点的最早发生时间;
  3. 只要栈不空,重复以下步骤:
    • 将栈顶顶点j出队;
    • 将栈顶j的每一个邻接点k,根据顶点k的vl(k)和弧的权值,更新顶点j的最晚发生时间vl(j)。
    • 扫描每一条弧计算其ee(i)和el(i),当ee(i)==el(i)输出该条弧。

具体代码有点难,暂时不做实现。


学习笔记:图的拓扑排序和关键路径_第8张图片

你可能感兴趣的:(学习笔记,数据结构,图论,数据结构,算法,拓扑学)