有向图 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值