大学里的专业课程居然靠算法安排?(附有动态图详解)

一、有向无环图简介

有向无环图:一个不形成闭环的有向图,简称DAG图

        作用:描述工程进行过程的有效工具,主要进行拓扑排序和关键路径的操作

大学里的专业课程居然靠算法安排?(附有动态图详解)_第1张图片

二、拓扑排序

对于学习计算机编程的同学来说,下面的课程应该不陌生吧。那这些课程究竟是如何安排的,学生才能循序渐进学习,顺利毕业?

答案:采用拓扑排序

大学里的专业课程居然靠算法安排?(附有动态图详解)_第2张图片

概念: 

  • 拓扑排序:由某个集合上的一个偏序得到该集合上的一个全序的操作。拓扑排序实际上是对邻接表表示的图进行深度优先遍历的过程

  • AOV——用顶点表示活动,用弧表示活动间优先关系的有向图称为顶点表示活动的网(Activity On Vertex network)简称AOV

那对于一个有向无环图如何用拓扑排序输出,请看下方动态演示:
大学里的专业课程居然靠算法安排?(附有动态图详解)_第3张图片

  1. 找到一个入度为0的顶点作为起点,上图为C0,删掉和C0相连的边
  2. 再找一个入度为0的点C1,如果有两个顶点入度为0,优先选择较小的,删除C1相连的边
  3. 重复上述操作,直到遍历完成

代码实现:

Status TopologicalSort(ALGraph G){
  //有向图G采用邻接表存储结构。
  //若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则ERROR。
  FindInDegree(G,indegree); //对各顶点求入度indegree[0..vernum-1]
  InitStack(S);
  for(i = 0;inextarc){
      k = p->adjvex;   //对i号顶点的每个邻接点的入度减1
      if(!(- - indegree[k])) Push(S,k);   //若入度减为0,则入栈
    }//for
  }//while
  if(count

特点总结:

  • 对邻接表时间复杂度O(n+e)
  • 针对有向无环图
  • 至少有一个入度为0和出度为0的顶点

三、关键路径

基本概念:

  • AOE(Activity On Edge)——也叫边表示活动的网。AOE网是一个带权的有向无环图,其中顶点表示事件,弧表示活动,权值表示活动持续时间。
  • 路径长度——路径上各活动持续时间之和
  • 关键路径——路径长度最长的路径
  • Ve(j)——表示事件Vj的最早发生时间
  • Vl(j)——表示事件Vj的最迟发生时间
  • e(i)——表示活动ai的最早开始时间
  • l(i)——表示活动ai的最迟开始时间
  • l(i)-e(i)——表示完成活动ai的时间余量
  • 关键活动——关键路径上的活动,即l(i)=e(i)的活动

案例分析:

大学里的专业课程居然靠算法安排?(附有动态图详解)_第4张图片

求解事件发生时间
顶点 Ve Vl
V1 0 0
V2 6 6
V3 4 6
V4 5 8
V5 7 7
V6 7 10
V7 16 16
V8 14 14
V9 18 18
  • Ve的值是起始点到Ve的最大值,从v1~v9开始写
  • Vl的值是V9减去路径的最大值,从v9~v1开始写
活动发生表
活动 e l l-e
a1 0 0 0
a2 0 2 2
a3 0 3 3
a4 6 6 0
a5 4 6 2
a6 5 8 3
a7 7 7 0
a8 7 7 0
a9 7 10 3
a10 16 16 0
a11 14 14 0
  • e表示边的起点Ve值
  • l表示边的终点Vl减去边的权值
  • l-e为0就是关键路径上的活动

演示图:

代码实现:

Status TopologicalOrder ( ALGraph G, SqStack &T ) {
// 有向图 G 采用邻接表存储结构,求各顶点事件的最早发生时间 ve(全局变量)。
// T 为拓扑序列顶点栈,S 为零入度顶点栈。
// 如果 G 无回路,则用栈 T 返回 G 的一个拓扑序列,且函数值为 OK,否则为 ERROR。
FindInDegree ( G, indegree ); // 对各顶点求入度 indegree [0..vernum-1]
InitStack (S); // 初始化零入度顶点栈 S
for ( i = 0; i < G.vexnum; ++i ) // 建立零入度顶点栈 S
if ( ! indegree [i] ) Push ( S, i ); // 入度为 0 的顶点序号入栈
InitStack (T); // 初始化拓扑序列顶点栈 T
count = 0; // 初始化输出顶点计数器
ve[0..G.vexnum-1] = 0; // 设各顶点最早发生时间为 0
while ( ! StackEmpty (S) ) {
	Pop ( S, j ); Push ( T, j ); ++count; // j 号顶点入 T 栈并计数
	for ( p = G.vertices[j].firstarc; p; p = p->nextarc ) 
  	 {	k = p->adjvex; // 对 j 号顶点的每个邻接点入度减 1
		if ( ! ( --indegree [k] ) ) Push ( S, k );  // 若入度减为 0 ,则入栈
		if ( ve[j] + *( p->info) > ve[k] ) // 求各顶点的最早发生时间
		    ve[k] = ve[j] + *( p->info);
    	 } // for 结束
} // while 结束
if ( count < G.vexnum ) return ERROR; // 该有向网有回路
else return OK;
} // ToptlogicalOrder
Status CriticalPath ( ALGraph G ) {
// G 为有向网,输出 G 的各项关键活动。
if ( ! TopologicalOrder ( G, T ) ) return ERROR;
vl[0..G.vexnum-1] = ve[G.vexnum-1]; // 初始化顶点事件最迟发生时间
while ( ! StackEmpty (T) ) // 按拓扑逆序求各顶点的 vl 值
	for ( Pop ( T, j ), p = G.vertices[j].firstarc; p; p = p->nextarc ) 
              {     k = p->adjvex; dut = *( p->info );
	        // dut 是事件 vj 到事件 vk 活动持续时间,即 dut
	if ( vl[k] – dut < vl[j] ) vl[j] = vl[k] – dut;
	} // for 结束
for ( j = 0; j < G.vexnum; ++j )
	// 求活动的最早开始时间 ee、最迟开始时间 el 和关键活动
for ( p = G.vertices[j].firstarc; p; p = p->nextarc )
 {	k = p->adjvex; dut = *( p->info );
	ee = ve[j]; el = vl[k] – dut;
	tag = ( ee = = el ) ? ¢ * ¢ : ¢ ¢ ;
	printf ( j, k, dut, ee, el, tag ); // 输出关键活动
} // for ( p = G.vertices[j].firstarc ) 结束
} // CriticalPath

总结:

  • 关键路径至少有一条,但不是只有一条。
  • 关键路径是指所有路径中总持续时间最长的路径

你可能感兴趣的:(数据结构与算法,算法)