一、拓扑排序
设G=(V,E)是一个具有n个顶点的有向图,V中顶点序列v1,v2,…,vn称为一个拓扑序列
当且仅当该顶点序列满足下列条件:
若<i,j>是图中的边(即从顶点i到j有一条路径),则在拓扑序列中顶点i必须排在顶点j之前。
在一个有向图中找一个拓扑序列的过程称为拓扑排序。
例如,计算机专业的学生必须完成一系列规定的基础课和专业课才能毕业,假设这些课程的名称与相应代号有如下关系:
课程之间的先后关系可用有向图表示:
对这个有向图进行拓扑排序可得到一个拓扑序列:C1-C3-C2-C4-C7-C6-C5。
也可得到另一个拓扑序列:C2-C7-C1-C3-C4-C5-C6,还可以得到其他的拓扑序列。
学生按照任何一个拓扑序列都可以顺序地进行课程学习。
拓扑排序步骤:
(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边。
(3)重复上述两步,直到剩余的网中不再存在没有前驱的顶点为止。
为了实现拓扑排序的算法,对于给定的有向图,采用邻接表作为存储结构,为每个顶点设立一个链表,每个链表有一个表头节点,这些表头节点构成一个数组,表头节点中增加一个存放顶点入度的域count。
即将邻接表定义中的VNode类型修改如下:
typedef struct //表头节点类型{ eertex data; //顶点信息
int count; //存放顶点入度
ArcNode *firstarc; //指向第一条弧
} VNode;
void TopSort(VNode adj[],int n)
{ int i,j;int St[MAXV],top=-1; //栈St的指针为top
ArcNode *p;
for (i=0;i<n;i++)
if (adj[i].count==0) //入度为0的顶点入栈
{ top++; St[top]=i; }
while (top>-1) //栈不为空时循环
{ i=St[top];top--; //出栈
printf("%d ",i); p=adj[i].firstarc;
while (p!=NULL)
{ j=p->adjvex; adj[j].count--;
if (adj[j].count==0)
{ top++; St[top]=j; }
p=p->nextarc; //找下一个相邻顶点
}
}
}
二、AOE网与关键路径
若用前面介绍过的带权有向图(DAG)描述工程的预计进度,以顶点表示事件,有向边表示活动,边e的权c(e)表示完成活动e所需的时间(比如天数),或者说活动e持续时间。
图中入度为0的顶点表示工程的开始事件(如开工仪式),出度为0的顶点表示工程结束事件。则称这样的有向图为AOE网(Activity On Edge)。
整个工程完成的时间为:从有向图的源点到汇点的最长路径,具有最大长度的路径叫关键路径。
“关键活动”指的是:该边上的权值增加,将使有向图上的最长路径的长度增加。
注意:在一个AOE网中,可以有不止一条的关键路径。
计算各事件的ee(v)如下:
ee(A)=0
ee(B)=ee(A)+c(a1)=6
ee(C)=ee(A)+c(a2)=4
ee(D)=ee(A)+c(a3)=5
ee(E)=MAX(ee(B)+c(a4),ee(C)+c(a5)}=MAX{7,5}=7
ee(F)=ee(E)+c(a7)=16
ee(G)=ee(E)+c(a8)=14
ee(H)=ee(D)+c(a6)=7
ee(I)=MAX{ee(F)+c(a10),ee(G)+c(a11),ee(H)+c(a9)}=MAX(18,18,11}=18
由此可知,关键活动有a11、a10、a8、a7、a4、a1,因此关键路径有两条:A-B-E-F-I和A-B-E-G-I。
(1)事件v的最早开始时间:规定源点事件的最早开始时间为0。定义图中任一事件v的最早开始时间(early event) ee(v)等于x、y、z到v所有路径长度的最大值
(2)事件v的最迟开始时间:定义在不影响整个工程进度的前提下,事件v必须发生的时间称为v的最迟开始时间(late event) ,记作le(v)。le(v)应等于ee(y)与v到汇点的最长路径长度之差
(3)活动a的最早开始时间e(a)指该活动起点x事件的最早开始时间,即:e(a)=ee(x)
(4)活动a的最迟开始时间l(a) 指该活动终点y事件的最迟开始时间与该活动所需时间之差 ,即:l(a)=le(y)-c(5)关键活动:对于每个活动a,求出d(a)=l(a)-e(a),若d(a)为0,则称活动a为关键活动。