拓扑排序是针对有向图而言的, 无向图中不存在拓扑排序的概念. 所谓拓扑排序, 即为有向图中全部顶点的一种线性排序. 这个线性排序结果需要满足一定的条件, 即对于有向图中的任意两个顶点 u u u和 v v v, 若图中存在有向边 u → v u \rightarrow v u→v, 则在拓扑排序结果中顶点 u u u一定位于 v v v的前面.
那么, 由拓扑排序的概念可以推出, 只有有向图中入度为0的顶点才能成为拓扑排序的第一个顶点. 这个原则也是解决有向图拓扑排序的一个指导性原则.
由于存在一些概念上的联系, 这里简单插入介绍一下 A O V AOV AOV网络的概念.
Activity On Vertex Network, 顶点表示活动, 边表示活动间的先后关系的网络图.拓扑排序是对 A O V AOV AOV网络的一种简化或者调度活动的执行顺序的过程.
由于网络中, 不同的活动之间可能存在相互依赖, 因此拓扑排序最终给出了一种合理地调度活动执行的先后顺序.如果所有活动之间存在着一种或多种合理的顺序,能够顺利完成全部活动, 则表明该 A O V AOV AOV网络为有向无回路图或有向无环图. 若某个活动A依赖活动B, 而活动B又依赖活动A, 则表明 A O V AOV AOV网络存在回路或向环. 因此无法合理的完成全部活动.
那么, 如何确定一个有向图是否具有拓扑排序, 也即是确定一个有向图是否存在回路或环呢? 这里给出执行拓扑排序的方法:
不断重复地寻找入度为0的顶点, 执行如下操作: 首先将该顶点作为拓扑排序的输出结果, 然后将该顶点及顶点关联的出边从图中删去. 循环结束条件为: 所有的顶点都已搜索完毕, 或着顶点未搜索完, 但是顶点的入度不为0, 无法删除.
入度(inDegree): 顶点的入度表示有向图中以该顶点为终点的边的个数; 出度(outDegree): 顶点的出度表示有向图中以该顶点为起点的边的个数. 因此, 所谓的顶点的出边是指以顶点为起点的边.
由拓扑排序推出的原则可知, 拓扑排序的第一个顶点即为入度为0的点, 那么在有向图中删除该顶点后的构成的新图, 重复地进行拓扑排序即可得到最终的拓扑排序结果.
具体到解决问题的方法上, 我们直到关于图的搜索一般有两种方式: 广度优先搜索和深度优先搜索.
e d g e s edges edges 邻接表集合
i n D e g r e e s inDegrees inDegrees 顶点的入度集合
b f s Q bfsQ bfsQ 搜索队列, 存放入度为0的顶点
t o p o L i s t topoList topoList 存储拓扑排序结果
BFS(G(V, E))
//初始化
for u in G(V)
for v in edges(u)
do inDegrees(v) += 1
//放入入度为0的顶点
for degree in inDegrees
do if degree == 0 then
bfsQ.push(i);
//循环搜索
while bfsQ not empty do
//搜索到入度为0的顶点
vertex = bfsQ.pop
//插入到拓扑排序结果中
topoList.add(vertex)
//搜索顶点vertex的邻接表
for v in edges(vertex)
//更新顶点v的入度值
inDegrees(v) -= 1
do if inDegrees(v) == 0
//顶点入度减为0时添加待搜索队列中
then bfsQ.push(v)
//搜索结束后, 判断结束条件
//当拓扑排序结果为全部的顶点时, 表明满足循环结束条件a, 否则满足循环结束条件b.
return topoList.size() == |V| ? topoList: 0;
DFS在搜索过程中顶点具有三种状态: 未搜索(0), 搜索中(1), 已搜索(2).
三种状态的含义如下:
未搜索(0): 表示搜索过程中该顶点尚未被发现, 属于一个新顶点,
搜索中(1): 表示该顶点已被发现, 但其邻接顶点还尚未被搜索完,
已搜索(2): 表示该顶点及其邻接顶点都完成搜索.
e d g e s edges edges 邻接表集合
v i s i t e d visited visited 用于保存顶点的搜索状态
S S S 栈, 保存拓扑排序结果
algorithm for DFS(G(V,E))
//初始化visited
for u in V[G]
do visited(u) = 0
for u in V[G]
do if visited(u) == 0 then
//深度优先搜索全部的顶点
DFS(u)
拓扑排序结果为S出栈的顺序, 原因是先完成搜索的顶点存放到栈的底部
DFS(u)
//顶点的状态设为搜索中
do visited(u) = 1
for v in edges(u)
//深度优先的方式进行邻接表的搜索
do if visited(v) == 0 then
//遇到未搜索状态的顶点, 继续进行深度优先搜索
DFS(u)
do if visited(v) == 1 then
//遇到搜索中状态的顶点, 说明此前该顶点已经在这条深度优先搜索路径中出现两次
//这里表明有向图中存在回路或环
do if visited(v) == 2 then
//pass 不重要的
trivial
//到这里说明顶点u已经完成深度优先搜索, 更新其搜索状态为已搜索
visited(u) == 2
//入栈
S.push(u)
力扣题解-207. 课程表
力扣题解-210. 课程表 II