有向无环图与关键路径

一、有向无环图

  即DAG(Directed Acycline Graph),为图中无环的有向图。

1.判断

①深度优先搜索
 可以使用DFS,找出是否存在环:从某个顶点 v 0 v_0 v0出发,进行DFS,若存在一条从顶点到已访问顶点 v v v的回边(即遍历到同一个点两次),则有向图中存在环。

②拓补排序

2.应用
①表达式共享

  可用于表示表达式的共享,相比于二叉树的表示形式更节省空间:

二叉树形式:
     

有向无环图形式:
         

②AOV网

  AOV(Activity On Vertex Network)网,对工程活动加以抽象,图中顶点表示活动,有向边表示活动之间优先关系,这样的有向图称AOV网。AOV网中不能有环,否则,某项活动能否进行是以自生是否完成作为前提条件的。
  可对AOV网进行拓补排序,以求得各子活动之间的优先关系。

二、拓补排序

  • 拓补有序序列:设 G = V , E G={V,E} G=V,E是为n阶有向图, V V V中的顶点序列 v 1 , v 2 , . . . , v n v_1,v_2,...,v_n v1,v2,...,vn,满足:若从顶点 v 0 v_0 v0 v j v_j vj有这样一条路径:顶点序列中 v 0 v_0 v0必在 v j v_j vj之前,这样的顶点序列称为一个拓补有序序列。

  • 拓补排序:拓补排序为对一个有向图构造拓补有序序列的过程;构造会有两个结果:

    1. 若全部顶点都被输出,则说明DAG不存在环(回路)。
    2. 若有的顶点没有被输出,则DAG存在环(回路)。

1.拓补排序算法
  1. 在有向图中选一个没有前驱的顶点作为始点
  2. 从图中删除该点与所有以它为尾的弧(改弧的弧头顶点入度减1)
  3. 重复①②,直到图中不再有没有前驱(入度为0)的顶点为止

如何选择入度为0的顶点
 使用栈或队列保存入度为0的顶点,新增InDegree记录各顶点的入度。


2.数据结构的实现
  1. 邻接表
     由于拓补排序过程中需要始终查找入度为0的顶点,故需为每个顶点结构新增一个入度域InDegree;且每遍历一个顶点需要对以其为尾的弧进行遍历,找到所有邻接点,再将他们的入度域减一。其间涉及大量链表遍历,显然使用邻接矩阵更为方便。

实现:

//数据结构
#define MAXVERTEXNUM 100

struct ArcNode{
	int AdjVex;
	ArcNode *NextArc;
};

struct VexNode{
	int InDegree;
	char Data;
	ArcNode *firstArc;
};

class Graph{
	int VertexNum;
	VexNode *AdjList;
}

//具体算法
void TopologicalSort(){
	stack<VexNode> S; 	//存放入度为零的顶点
	VexNode v;
	ArcNode* w;
	int i,count;
	for(int i=0;i<VertexNum;i++)
		if(!AdjList[i].InDegree)	S.push(AdjList[i]);	//入度为零的顶点入栈
	count=0;		//计数器置0
	while(!S.empty()){
		v=S.top();
		S.pop();
		count++;
		cout<<v.Data<<" ";
		for(w=v.fristArc;w;w=w->NextArc){
			AdjList[w->AdjVex].InDegree--;
			if(!AdjList[w->AdjVex].InDegree)
				S.push(AdjList[w->AdjVex];)
		}
	}
	if(cout<VertexNum)	cout<<"图中含有回路";
	cout<<endl;
}


2. 邻接矩阵
  ①逐列扫描矩阵,找到入度为0的顶点 v v v
  ②输出顶点 v v v,标识 v v v已访问
  ③将矩阵第 v v v行全部清0(删除所有以该顶点为尾的弧,且剪掉对应弧头顶点的度数)
  ④重复上述不走,直到没有入度为0的顶点

以上方法矩阵最终所有的元素不一定全为0。


三、关键路径

  对于工程活动,人们不仅关心工程能否顺利完成(对AOV网进行拓补排序),还关心:

  • 估算整个工程完成所需最短时间。
  • 影响工程的关键活动是什么,那些活动的延期将会影响整个工程的进度,加速这些活动是否会提高整个工程的效率。
1.参数介绍


1.AOE网(Activity On Edge NetWork):以顶点表示事件(Event),以有向边表示活动,边上的权值为该活动持续的事件,这样的有向图称为AOE网。利用AOE网可完成以上工作。

  • 只有某顶点事件发生后,从该顶点出发的各有向边活动才能开始
  • 只有进入某顶点的所有边的各顶点所代表的活动都已结束,该顶点事件才能发生。
  • 在AOE网中,时间不能自己完成,而是由活动驱动,随活动的完成而完成。


2.关键路径

  • 把AOE网中某路径上各个活动所持续时间之和称为路径长度
  • 从源点到汇点的最大程度的路径称为关键路径其长度为工程完成的最短时间。关键路径可能不唯一。
  • 关键路径上的活动称为关键活动,关键活动是影响整个工程时间的关键所在。
  • 顶点:工程需要完成的第一个事件
  • 汇点:工程需要完成的最后一个事件

介绍关键路径算法之前,先介绍几个重要参数:

  • V e ( j ) V_e(j) Ve(j):表示事件 j j j最早发生事件,即从源点到顶点 j j j的最长路径长度(只有将所有所需的事件都完成了 j j j才能进行);
  • V l ( k ) V_l(k) Vl(k):表示事件 k k k最晚发生时间,即从汇点到顶点 k k k的最短路径长度(此时事件 k k k再不发生则会影响工程的进度);
  • e ( a i ) e(a_i) e(ai):表示活动 a i a_i ai的最早开始时间;
  • i ( a i ) i(a_i) i(ai):在不影响进度的前提下,活动 a i a_i ai的最晚开始时间;
  • d u t ( < j , k > ) dut(<j,k>) dut(<j,k>):弧 < j , k > <j,k> <j,k>所对应活动的持续时间;

其中 i ( a i ) − e ( a i ) i(a_i)-e(a_i) i(ai)e(ai)表示活动 a i a_i ai的时间余量,即可以推迟多久执行而不影响工程的进度;若 ( a i ) − e ( a i ) = 0 (a_i)-e(a_i)=0 (ai)e(ai)=0,则表示活动 a i a_i ai为关键活动,即该活动在关键路径上;于是求关键路径的问题就转换成了求各活动的 e ( a i ) e(a_i) e(ai) l ( a i ) l(a_i) l(ai),求出所有关键活动后可得关键路径。


各参数间的关系

  • e ( a i ) = V e ( j ) e(a_i)=V_e(j) e(ai)=Ve(j),即活动 a i a_i ai的弧尾时间 j j j的最早发生时间,当事件 j j j发生了,活动 a i a_i ai最早可以现在开始;
  • l ( a i ) = V l ( k ) − d u t ( < j , k > ) l(a_i)=V_l(k)-dut(<j,k>) l(ai)=Vl(k)dut(<j,k>),即活动 a i a_i ai的弧头事件 k k k的最晚发生时间减去活动 a i a_i ai的持续时间——此时活动 a i a_i ai再不发生,则会导致事件 k k k推迟,从而事件 k k k发生时间晚于最晚时间,导致工程延期。

于是关键路径的求解又转换为求各顶点最早、最晚发生时间。



各参数的值

1.事件最早开始时间
V e ( j ) = { 0 , 若 j 为 源 点 M a x { V e ( x ) + d u t ( < x , j > ∣ < x . j > 为 图 中 的 弧 ) } V_e(j)=\begin{cases} 0,若j为源点\\ Max\{V_e(x)+dut(<x,j>|<x.j>为图中的弧)\}\\ \end{cases} Ve(j)={0jMax{Ve(x)+dut(<x,j><x.j>)}
 除源点外,只有进入顶点 j j j的所有弧所代表的活动全部结束后,事件 j j j才能发生。即只有 j j j的所有前驱事件 x x x的最早发生时间 V e ( x ) V_e(x) Ve(x)计算出来后,才能计算 V e ( j ) V_e(j) Ve(j)



2.事件最晚开始时间
V l ( k ) = { V e ( n − 1 ) , 若 k 是 汇 点 M i n { V l ( y ) − d u t ( < k , y > ∣ < k , y > 为 图 中 的 弧 ) } V_l(k)=\begin{cases} V_e(n-1),若k是汇点\\ Min\{V_l(y)-dut(<k,y>|<k,y>为图中的弧)\} \end{cases} Vl(k)={Ve(n1)kMin{Vl(y)dut(<k,y><k,y>)}
 只有 k k k的所有后继事件 y y y的最晚发生时间 V l ( y ) V_l(y) Vl(y)计算出来后,才能计算 V l ( k ) V_l(k) Vl(k),此时事件 k k k再不发生,则会延误后续的事件。

2.具体算法
  1. 利用拓补排序求出AOE网的一个拓补序列
  2. 从拓补序列的源点开始,令 V e [ 0 ] = 0 V_e[0]=0 Ve[0]=0,按拓补顺序依次计算每个事件的最早发生时间 V e ( j ) V_e(j) Ve(j)
  3. 从拓补序列的汇点开始,令 V l [ n ] = V e [ n − 1 ] V_l[n]=V_e[n-1] Vl[n]=Ve[n1](终点一定在关键路径上),按逆拓补顺序依次计算每个事件的最晚发生时间 V l ( j ) V_l(j) Vl(j)
  4. 计算每个活动 a i a_i ai的最早开始时间 e ( a i ) e(a_i) e(ai)和最晚开始时间 l ( a i ) l(a_i) l(ai),若 e ( a i ) = l ( a i ) e(a_i)=l(a_i) e(ai)=l(ai),则该活动 a i a_i ai为关键活动,该弧所依附的顶点序列为关键路径,关键路径上的权值之和为完成工程所需的最短时间。
  • 只有缩短关键活动时间才能缩短整个工程工期
  • 一个项目可以有多个、并行的关键路径。若一个关键活动不在所有的关键路径上,减少它并不能减少工期。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python是一种流行的编程语言,可以用来处理图论问题。在Python中,可以使用图的数据结构来表示和操作有向无环图(Directed Acyclic Graph,DAG)。有向无环图是一种图结构,其中的边都是有方向的,并且不存在任何形成环路的路径。 在Python中,可以使用各种图论库来处理有向无环图,例如networkx、graph-tool等。这些库提供了一系列的函数和方法,可以用来创建、操作和分析有向无环图。 具体来说,要判断一个图是否是有向无环图,可以使用拓扑排序算法。拓扑排序是一种对有向无环图进行排序的算法,它将图中的节点按照依赖关系进行排序,使得每个节点在排列中都位于它的依赖节点之后。 在Python中,可以使用拓扑排序算法来判断一个图是否是有向无环图。如果拓扑排序成功完成,并且没有环路存在,则可以确定该图是有向无环图。 下面是一个示例代码,演示了如何使用Python和networkx库来判断一个图是否是有向无环图: ```python import networkx as nx # 创建一个有向无环图 G = nx.DiGraph() G.add_edges_from([(1, 2), (2, 3), (3, 4)]) # 判断图是否是有向无环图 if nx.is_directed_acyclic_graph(G): print("该图是有向无环图") else: print("该图不是有向无环图") ``` 在上面的示例中,我们首先使用networkx库创建了一个有向无环图G,然后使用`is_directed_acyclic_graph`函数判断该图是否是有向无环图。如果返回结果为True,则说明该图是有向无环图,否则说明该图不是有向无环图。 希望这个示例对你有所帮助。如果你需要更多关于有向无环图的信息,可以参考networkx库的文档或者其他相关资料。<span class="em">1</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值