有向图 G=(V, E) 的拓扑排序

有向图 G = ( V , E ) G=(V,E) G=(V,E) 的拓扑排序

拓扑排序概念

拓扑排序是针对有向图而言的, 无向图中不存在拓扑排序的概念. 所谓拓扑排序, 即为有向图中全部顶点的一种线性排序. 这个线性排序结果需要满足一定的条件, 即对于有向图中的任意两个顶点 u u u v v v, 若图中存在有向边 u → v u \rightarrow v uv, 则在拓扑排序结果中顶点 u u u一定位于 v v v的前面.

那么, 由拓扑排序的概念可以推出, 只有有向图中入度为0的顶点才能成为拓扑排序的第一个顶点. 这个原则也是解决有向图拓扑排序的一个指导性原则.

A O V AOV AOV网络

由于存在一些概念上的联系, 这里简单插入介绍一下 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的点, 那么在有向图中删除该顶点后的构成的新图, 重复地进行拓扑排序即可得到最终的拓扑排序结果.

具体到解决问题的方法上, 我们直到关于图的搜索一般有两种方式: 广度优先搜索和深度优先搜索.

广度优先搜索步骤

  1. 定义inDegree用于存储搜索过程中每一个顶点的入度.
  2. 遍历一边图的邻接表, 将每一个顶点的邻接表转化为对应顶点的入度.
    具体地, 对于一条有向边 u → v u \rightarrow v uv, 则inDegree(v) += 1.
  3. 选择入度为0的顶点开始搜索, 将顶点作为拓扑排序输出.
  4. 对于已经完成搜索的顶点, 更新其邻接表中顶点的入度, 值减一.
  5. repeat 3,4, until 循环结束条件.
  6. 循环结束条件: a. 搜索完有向图中全部顶点, b. 无法完成全部顶点的搜索
  7. 满足条件a时, 表明有向图存在拓扑排序结果; 满足条件b时, 则表明该有向图存在回路或环, 不存在拓扑排序.

BFS Pseudocode

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): 表示该顶点及其邻接顶点都完成搜索.

DFS Pseudocode

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

你可能感兴趣的:(algorithm,算法,有向图,拓扑学,dfs,bfs)