拓扑排序
一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(Activity)。在整个工程中,有些子工程(活动)必须在其他有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种用顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
例如,假定一个计算机专业的学生必须完成表7-1所列出的全部课程。在这里,课程代表活动,学习一门课程就表示进行一项活动,学习每门课程的先决条件是学完它的全部先修课程。如学习《数据结构》课程就必须安排在学完它的两门先修课程《离散数学》和《算法语言》之后。学习《高等数学》课程则可以随时安排,因为它是基础课程,没有先修课。
![0787c5f40ffaf943efd0159636a71951.png](https://i-blog.csdnimg.cn/blog_migrate/ab033c5ec35ee366e8325388859166e8.jpeg)
若用AOV网来表示这种课程安排的先后关系,则如图7-15所示。图中的每个顶点代表一门课程,每条有向边代表起点对应的课程是终点对应课程的先修课。从图中可以清楚地看出各课程之间的先修和后续的关系。如课程C5的先修课为C2,后续课程为C4和C6;C6的先修课为C4和C5,它无后续课。
![93878bc8dddf8e8b33c493ac992734c4.png](https://i-blog.csdnimg.cn/blog_migrate/06870ffaca013af08443e34749183e5e.jpeg)
一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行。如图7-16是一个具有三个顶点的回路,由边可得B活动必须在A活动之后,由边可得C活动必须在B活动之后,所以推出C活动必然在A活动之后,但由边可得C活动必须在A活动之前,从而出现矛盾,使每一项活动都无法进行。这种情况若在程序中出现,则称为死锁或死循环,是必须避免的。
在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological Order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological Sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
例如,下面的三个序列都是图7-15的拓扑序列,当然还可以写出许多。
(1) C1,C8,C9,C2,C3,C5,C4,C7,C6
(2) C2,C1,C3,C5,C4,C6,C8,C9,C7
(3) C1,C2,C3,C8,C9,C5,C4,C6,C7
由AOV网构造出拓扑序列的实际意义是:如果按照拓扑序列中的顶点次序进行每一项活动,则就能够保证它的所有前驱活动此前都已完成,从而使整个工程顺序进行,不会出现冲突的情况。
由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
(1)选择一个入度为0的顶点并输出之;
(2)从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。
下面以图7-17(a)为例,来说明拓扑排序算法的执行过程。
![9614bedc170bec92cd21163838bc5df6.png](https://i-blog.csdnimg.cn/blog_migrate/da4d21791aefafe74fdec8c87d4761b3.jpeg)
拓扑排序的图形说明
![39859022c605929e084e284ef62f7513.png](https://i-blog.csdnimg.cn/blog_migrate/4092a2feabf4c5db6e19378936c6846c.jpeg)
(1)在(a)图中v0和v1的入度都为0,不妨选择v0并输出之,接着删去顶点v0及出边<0,2>,得到的结果如图(b)所示。
(2)在图(b)中只有一个入度为0的顶点v1,输出v1,接着删去v1和它的三条出边<1,2>、<1,3>和<1,4>,得到的结果如图(c)所示。
(3)在图(c)中v2和v4的入度都为0,不妨选择v2并输出之,接着删去v2及两条出边<2,3>和<2,5>,得到的结果如图(d)所示。
(4)在图(d)上依次输出顶点v3、v4和v5,并在每个顶点输出后删除该顶点及出边,操作都很简单,不再赘述。x