研究了一个月的「拓扑排序」,给大家说一说

本文深入探讨了拓扑排序的概念,从有向无环图(DAG)出发,解释了拓扑排序的含义和应用场景,如课程选修的顺序。通过实例分析,展示了如何手动找到一个图的拓扑排序,并详细介绍了使用入度计算的拓扑排序算法,包括步骤、数据结构选择和时间复杂度分析。此外,还提到了拓扑排序在关键路径问题和项目管理中的应用。
摘要由CSDN通过智能技术生成

前言

Topological sort 又称 Topological order,这个名字有点迷惑性,因为拓扑排序并不是一个纯粹的排序算法,它只是针对某一类图,找到一个可以执行的线性顺序。

这个算法听起来高大上,如今的面试也很爱考,比如当时我在面我司时有整整一轮是基于拓扑排序的设计。

但它其实是一个很好理解的算法,跟着我的思路,让你再也不会忘记她。

有向无环图
刚刚我们提到,拓扑排序只是针对特定的一类图,那么是针对哪类图的呢?

答:Directed acyclic graph (DAG),有向无环图。即:

这个图的边必须是有方向的;

图内无环。

那么什么是方向呢?

比如微信好友就是有向的,你加了他好友他可能把你删了你却不知道。。。那这个朋友关系就是单向的。。

什么是环?环是和方向有关的,从一个点出发能回到自己,这是环。

所以下图左边不是环,右边是。

img

那么如果一个图里有环,比如右图,想执行1就要先执行3,想执行3就要先执行2,想执行2就要先执行1,这成了个死循环,无法找到正确的打开方式,所以找不到它的一个拓扑序。

总结:
如果这个图不是 DAG,那么它是没有拓扑序的;

如果是 DAG,那么它至少有一个拓扑序;

反之,如果它存在一个拓扑序,那么这个图必定是 DGA.

所以这是一个充分必要条件。

img

拓扑排序
那么这么一个图的「拓扑序」是什么意思呢?

我们借用百度百科[1]的这个课程表来说明。

课程代号 课程名称 先修课程
C1 高等数学 无
C2 程序设计基础 无
C3 离散数学 C1, C2
C4 数据结构 C3, C5
C5 算法语言 C2
C6 编译技术 C4, C5
C7 操作系统 C4, C9
C8 普通物理 C1
C9 计算机原理 C8
这里有 9 门课程,有些课程是有先修课程的要求的,就是你要先学了「最右侧这一栏要求的这个课」才能再去选「高阶」的课程。

那么这个例子中拓扑排序的意思就是:
就是求解一种可行的顺序,能够让我把所有课都学了。

那怎么做呢?

首先我们可以用图来描述它,图的两个要素是顶点和边,那么在这里:

顶点:每门课

边:起点的课程是终点的课程的先修课

画出来长这个样:

img

这种图叫 AOV (Activity On Vertex) 网络,在这种图里:

顶点:表示活动;

边:表示活动间的先后关系

所以一个 AOV 网应该是一个 DAG,即有向无环图,否则某些活动会无法进行。

那么所有活动可以排成一个可行线性序列,这个序列就是拓扑序列。

那么这个序列的实际意义是:

按照这个顺序,在每个项目开始时,能够保证它的前驱活动都已完成,从而使整个工程顺利进行。

回到我们这个例子中:

我们一眼可以看出来要先学 C1, C2,因为这两门课没有任何要求嘛,大一的时候就学呗;

大二就可以学第二行的 C3, C5, C8 了,因为这三门课的先修课程就是 C1, C2,我们都学完了;

大三可以学第三行的 C4, C9;

最后一年选剩下的 C6, C7。

这样,我们就把所有课程学完了,也就得到了这个图的一个拓扑排序。

注意,有时候拓扑序并不是唯一的,比如在这个例子中,先学 C1 再学 C2,和先 C2 后 C1 都行,都是这个图的正确的拓扑序,但这是两个顺序了。

所以面试的时候要问下面试官,是要求解任意解,还是列出所有解。

我们总结一下,在这个图里的边表示的是一种依赖关系,如果要修下一门课,就要先把前一门课修了。

这和打游戏里一样一样的嘛,要拿到一个道具,就要先做 A 任务,再完成 B 任务,最终终于能到达目的地了。

算法详解
在上面的图里,大家很容易就看出来了它的拓扑序,但当工程越来越庞大时,依赖关系也会变得错综复杂,那就需要用一种系统性的方式方法来求解了。

那么我们回想一下刚刚自己找拓扑序的过程,为什么我们先看上了 C1, C2?

因为它们没有依赖别人啊,也就是它的入度为 0.

入度:顶点的入度是指「指向该顶点的边」的数量;
出度:顶点的出度是指该顶点指向其他点的边的数量。

所以我们先执行入度为 0 的那些点,那也就是要记录每个顶点的入度。

因为只有当它的 入度 = 0 的时候,我们才能执行它。

在刚才的例子里,最开始 C1, C2 的入度就是 0,所以我们可以先执行这两个。

那在这个算法里第一步就是得到每个顶点的入度。

Step0: 预处理得到每个点的入度
我们可以用一个 HashMap 来存放这个信息,或者用一个数组会更精巧。

在文中为了方便展示,我就用表格了:

C1	C2	C3	C4	C5	C6	C7	C8	C9

入度 0 0 2 2 1 2 2 1 1

Step1
拿到了这个之后,就可以执行入度为 0 的这些点了,也就是 C1, C2.

那我们把可以被执行的这些点,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值