注:学习自
严蔚敏《数据结构》
李春葆《数据结构》
一丶了解AOE
AOE(Activity On Edge)网:用来估算完成事件时间
顶点表示事件,边表示活动,权表示活动持续时间。是一个带权的有向无环图
如图,学习完计算机基础到开始学习数据结构需要2天,学习完计算机基础到开始学习计算机原理需要20天,后面同理,可以看出学到操作系统必须30天。
二丶AOE 与 关键路径
有向图是描述一项工程或系统的进行过程
的有效工具,几乎所有的工程都可以分为若干个子工程,而这些子工程之间,通常受着一定约束,比如事件先后约束,每个活动完成的时间
-
人们在做工程时关注两个问题
-
- 一是工程是否能顺利进行,就是从AOE网中使用
拓扑排序
,检测出图中是否有环,没有环的话可以依照得到的序列去完成该项目,比如软件工程学习课程可以按照 计算机基础,数据结构,计算机原理,操作系统来学
-
为什么有环就不能顺利进行呢?
-
大家可以思考就像下面的例子我们应该从何处开始呢?是蛋吗?那必须要有母鸡 ; 是雏鸡吗?那必须要有蛋 ; 是母鸡吗?那必须要有雏鸡。显然,这里的每项活动都是以自己为先决条件,这是不可能的,无从下手,死循环
- 二是工程必须最短时间完成,从AOE网中使用
关键路径
,可以看出事件完成时间,比如软件工程学习课程关键路径是,计算机基础,计算机原理,操作系统,这花费30天。分析关键路径我们可以提高关键活动的工效,缩短整个周期,从计算机基础到计算机原理时间太长了,肯定是去玩了,假如不玩,就直接缩短到3天,整个学习过程可以只用13天完成。所以得到关键路径就是为了分析是否可以虽短从而缩短工期
- 一是工程是否能顺利进行,就是从AOE网中使用
三丶拓扑排序:工程是否能顺利进行
拓扑排序
就是从一个有向图中找到一个拓扑序列
的过程。找到了拓扑序列也就是拓扑排序完成,工程可以顺利进行
-
什么是拓扑序列,以及怎么找到
-
图中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前(注<u,v>与<v,u>不是同一条边)。就如下图中,<1,2> < 3 ,2> 2事件必须出现在1事件与3事件之前,只有完成了1事件与3事件才能进行2事件,有先后次序
图中的边有 <1,2> 、<3,2> 、<2,5>、<3,4>、 <4,5> 、<2,6> 、<3,6>
我们就开始进行排序:132645 ,134265,132456……很多都是拓扑序列
这样凌乱的猜想是不能忍受的,我们需要找到一个规范的算法去寻找拓扑序列,如下
-
由AOV网构造拓扑序列的拓扑排序算法主要是
循环执行以下两步
,直到不存在入度为0的顶点为止
。
(1)
选择一个入度为0的顶点并输出之;
(2)
从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则说明AVO网有回路,否则输出的顶点序列就是一种拓扑序列。这里得到的排序并不是唯一的
Status TopologicalSort(ALGraph G) { // 算法7.12
// 有向图G采用邻接表存储结构。
// 若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则ERROR。
SqStack S;
int count,k,i;
ArcNode *p;
char indegree[MAX_VERTEX_NUM];
FindInDegree(G, indegree); // 对各顶点求入度indegree[0..vernum-1]
InitStack(S);
for (i=0; i<G.vexnum; ++i) // 建零入度顶点栈S
if (!indegree[i]) Push(S, i); // 入度为0者进栈
count = 0; // 对输出顶点计数
while (!StackEmpty(S)) {
Pop(S, i);
printf(i, G.vertices[i].data); ++count; // 输出i号顶点并计数
for (p=G.vertices[i].firstarc; p; p=p->nextarc) {
k = p->adjvex; // 对i号顶点的每个邻接点的入度减1
if (!(--indegree[k])) Push(S, k); // 若入度减为0,则入栈
}
}
if (count<G.vexnum) return ERROR; // 该有向图有回路
else return OK;
} // TopologicalSort
注:对于你拓扑序列,就是DFS函数最先出栈的顶点即为出度为0的顶点,是拓扑序列的最后一个顶点,由此按照DFS出栈的顺序记录就是逆拓扑序列
四丶关键路径:工程必须最短时间完成
AOE网中的有些活动是可以并行的进行,所以完成工程的最短时间
点最长路径的长度,对你没有看错,最长路径是最短时间,所以找关键路径就是找到最长路径所经过的点不难看出, 1 - 2 - 5是关键路径,关键路径长度为14天。
-
肉眼的观察不能忍受的,我们需要找到一个规范的算法去寻找关键路径,如下
举例
实现
Status TopologicalSort_Modify(ALGraph G, Stack &T) { // 算法7.13
// 有向网G采用邻接表存储结构,求各顶点事件的最早发生时间ve(全局变量)。
// T为拓扑序列定点栈,S为零入度顶点栈。
// 若G无回路,则用栈T返回G的一个拓扑序列,且函数值为OK,否则为ERROR。
Stack S;int count=0,k;
char indegree[40];
ArcNode *p;
InitStack(S);
FindInDegree(G, indegree); // 对各顶点求入度indegree[0..vernum-1]
for (int j=0; j<G.vexnum; ++j) // 建零入度顶点栈S
if (indegree[j]==0) Push(S, j); // 入度为0者进栈
InitStack(T);//建拓扑序列顶点栈T
count = 0;
for(int i=0; i<G.vexnum; i++) ve[i] = 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] == 0) Push(S, k); // 若入度减为0,则入栈
if (ve[j]+p->info > ve[k]) ve[k] = ve[j]+p->info;
}//for *(p->info)=dut(<j,k>)
}//while
if (count<G.vexnum) return ERROR; // 该有向网有回路
else return OK;
} // TopologicalOrder
Status CriticalPath(ALGraph G) {
// G为有向网,输出G的各项关键活动。
Stack T;
int a,j,k,el,ee,dut;
char tag;
ArcNode *p;
if (!TopologicalSort_Modify(G, T)) return ERROR; //用拓扑排序检测有无环
for(a=0; a<G.vexnum; a++)
vl[a] = 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<j,k>
if (vl[k]-dut < vl[j]) vl[j] = vl[k]-dut;
}
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); // 输出关键活动
}
return OK;
} // CriticalPath