拓扑排序---图

拓扑排序—图

1 简介

关于拓扑排序是这样定义的:

给定一个包含n个节点的有向图G,我们给出它的节点编号的一种排列,如果满足: 对于图G中的任意一条有向边 (u, v),u在排列中都出现在 v的前面。 那么称该排列是图G的拓扑排序。

若有向图中存在环路,则图G的拓扑排序不存在。原因是,有图G(u,v)(v,u),形成环路。写出遍历序列是u, v, u; 根据拓扑排序的定义,有向边(u,v)需要满足u,在v前,根据获得的排序结果u, v, u可见,存在环路的有向图不存在拓扑排序。

拓扑排序还有一个特点就是,排序的结果不唯一,极端的情况就是当有向图G中的节点没有一条边时,排序结果可以是任何顺序。

具体正确的有向图拓扑排序可见下节实现的例子。

2 实现思想

2.1 深度优先遍历(DFS)

深度优先的思想就是不断的向子节点遍历下去,直到子节点为空为止。当我们使用dfs实现拓扑排序时,需要为当前节点设置3个状态,分别表示当前节点还未搜索过、当前节点正在搜索,以及当前节点已经搜索完毕。

根据父节点在子节点前,以及深度优先的特点,我们要实现有向图的拓扑排序,可以采用栈来保存已经遍历过的节点。这样能够保证已经遍历过的节点总在栈底,父节点总在其上方,出栈时可以保证父节点在子节点前。

当我们明确采用dfs遍历图,以及采用栈保存已搜索的节点后,我们可以来理以下实现的思路。

1、从图中任一个节点开始遍历,将该节点状态置为正在搜索中,若当前节点存在一条边指向其它节点,则搜索当前指向的节点,重复上述过程直到一个节点没有子节点为止。

2、判断该节点是否已经被搜索过,若没有则将该节点状态置为已搜索,并将节点入栈。

3、若当前节点状态为正在搜索,说明遇到了环路(在步骤一已经将父节点置为正在搜索),直接返回该有向图不存在拓扑排序。

下面使用一个例子说明上述步骤:

存在有向图G如下:

1、从A节点开始遍历,使用黄色表示当前节点正在搜索,使用绿色表示已搜索,使用白色表示为搜索。A节点存在子节点,继续搜索其子节点(任选一个子节点即可)

2、我们选择搜D节点,D节点存在子节点,继续遍历子节点。

3、遍历到E节点,E节点已经不存在子节点,故将其入栈,并将状态置为已搜索。

4、回溯到D节点,D节点已不存在子节点未遍历,故将D入栈。

5、回溯到A节点,A节点存在子节点未遍历,继续遍历其子节点。

6、节点C不存在子节点,将其入栈。

7、回溯到A节点,A节点不存在子节点未遍历,故将A入栈。

8、遍历到A的兄弟节点,B不存在未遍历的子节点,故将B入栈。

最终得到的拓扑排序是:BACDE。

2.2 广度优先遍历(BFS)

广度优先搜索实现的拓扑排序过程,与深度优先搜索实现的过程刚好相反。深度优先搜索是将后面的节点放入栈底,广度优先搜索则是将前面的节点线全部遍历,在遍历后面的节点。所有广度优先搜索使用的是队列来实现拓扑排序。

其具体思想与实现步骤如下:

1、将所有入度为0即没有父节点指向的节点加入结果。

2、将刚刚加入结果的节点,所有边都删除,继续寻找入度为0的节点。

3、重复以上所有两个步骤,直到结果中节点数等于图的节点数,若不等,则说明该图洲存在环路。

还是使用上述例子说明bfs的流程:

存在有向图G如下:

1、将所有入度为0的节点加入队列。

2、将队首元素出队,加入结果中,并且将其所有边删除。

3、再次将当前所有入度为0的节点加入队列。

4、队首元素再次出队,删除对于的边。

5、再次将入度为0的节点入队

6、再次出队

7、再次入队

8、省略几步,直接全部出队,结果是:BADCE

用递归可以很容易实现该过程。接下来我们使用leetcode上的一道题说明,拓扑排序可以解决怎样的问题,以及给出拓扑排序的代码实现。

3 课程表问题

课程表问题,原题在LeetCode.210, 题意如下:

现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。

可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。

输入: 2, [[1,0]] 
输出: [0,1]
解释: 总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。

输入: 4, [[1,0],[2,0],[3,1],[3,2]]
输出: [0,1,2,3] or [0,2,1,3]
解释: 总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
    因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。

经过我们对拓扑排序的学习,思路应该很清晰,就是节点的拓扑排序问题。当存在环路是直接返回一个空数组即可。我们把先修课程作为父节点,指向后修课程,将每个课程这样处理后就形成一个有向图。

3.1DFS 代码实现

使用栈辅助

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值