tarjan算法_纯numpy的深度优先搜索+强联通分量+拓扑排序算法

比较硬核的开头:

最近因为要用到一些图拓扑排序的问题,因此复习了一些图算法,并用python实现了一下。这里先放出全部代码(包括带注释的py文件和可以运行的jupyter文件)的下载参考地址(封面图是repo中用到的例子):https://github.com/zhouyanasd/Topological_sorting_tarjan

8f4838ee26246f6957ad6da8f1cef7b5.png

比较硬核的原理解释:

目录

  1. 深度优先搜索 (Depth-first search, DFS)
  2. 强联通分量(Strongly connected components)
  3. 拓扑排序(Topological sorting)

1. 深度优先搜索 (DFS)

深度优先搜索是数据结构的图算法中相当基础的部分,核心思想是从根节点开始(可以从图中任意选择,但是不同的根节点搜索后得到的子树不一样),递归对它连接的后续节点进行访问。先拿出《算法导论》中的伪代码和例子。

伪代码

61b2043cc8963de3d4d6e20130f324b1.png

例子:

18e13f63e85052778cafa2b19681fd62.png

结合伪代码和例子,DFS首先初始化所有节点为白色(未被访问过),选择u为根节点开始访问,v为下一个被访问的子节点。当节点访问时,标记为灰色(正在被访问),当子节点为白色时则递归地对其进行访问。当没子节点可以访问时,则层层退出递归,也叫做回溯,如图(f),即回溯到某个灰色节点时,将其状态变为黑色(完成访问)。算法会维护一个访问step,节点变灰时记录其发现时间,当变成黑色时记录完成时间。如图(g)的y节点的发现时间是3,完成时间是6。


2. 强联通分量(Strongly connected components)

首先看一个什么是强连通分量:

有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

换句话说强连通分量就是有向图中的环,所有可以成环的点,只要能接触到其中一个,就可以通过链接到达任意一个。

那么从上面的例子可以看到,这里的强联通分量是(u,x,v,y),(w)和(z)三个。

但是我们要用搜索的办法来找到他们,主要的方法有3个:

  1. Kosaraju算法
  2. Tarjan算法
  3. Gabow算法

Kosaraju算法:

这个比较好理解,就是在第一次DFS之后,将图的方向反转,从最晚finish的节点开始再进行一次DFS。具体这里不做解释了,网上和算导中有很多帮助理解的资料。

Tarjan算法和Gabow算法:

这两个算法是对Kosaraju的一个改进,仅用了一次DFS即可找到强连通分量。

这里重点介绍一下Tarjan算法:

算法基本上是在DFS上进行了改进,首先算法引入了一个堆栈stack,和一个数组low。stack用来存储访问过的节点,low用来记录当前节点所属连通分量的根。

算法伪代码:

1fc7acfb5c5671cf60f8f2c30f3ae43a.png
来自维基百科

以上面的图为例,从u开始直到x(图e),算法中状态数组和stack如下:

13a82921ba201f7d0b7e7106745a7091.png

ef3c5c12a351199faf65f4b4ba8275e4.png

这时发现x的子节点u已经被访问,这时需要比较x的low和u的discorvary,取小值即low(u)=discorvary(u)=1。那么自此开始回溯,每当回溯到一个节点时,比较当前节点和后续节点的low值,取小值。因此不难判断,节点x、y、v的low都变成和x一样为1。这时的状态数组为:

88751d69018570d85ac6d4778ffeb09f.png

这时我们来到图j,由于伪代码中需要判断当low和discorvary相同时才可以完成搜索,所以这里和DFS的区别在于回溯后并没有立刻结束节点搜索,而节点仍然存在stack中。当回溯到u时,low(u)==discorvary(u),因此开始进行出栈。在栈顶是x!=u,所以继续出栈,直到u出栈后,将这些出栈的节点作为一个强连通分量进行记录。

接下来继续遍历w,过程基本上和之前相似,这里省略啦。

Gabow算法

Gabow是Tarjan算法的改进版,只是它用第二个栈来代替Tarjan算法里面的low和u的discorvary数组。具体这里暂时没有实现,不多做介绍啦。

3. 拓扑排序(Topological sorting)

终于来到重点了,前面做了这么多,其实就是为了进行排序。我们都知道,拓扑排序是只有在有向无环图(DAG)中才能进行,但是有环图是没办法直接进行拓扑排序的。

如何解决呢?可以先将图中的环(即强连通分量)找出,进行缩点(即将强联通分量看做为一个点),这样就可以保证缩点后的图中不存在环,也就可以进行拓扑排序了。

对于DAG,拓扑排序的顺序即为finish时刻的倒序,对于有环图来说,强联通分量组成的”大节点“中的finish一般是连续的,因此可以任意找出一个点来代表(可以是搜索中作根的节点)来进行排序。

另外,如果记录强连通分量的数据结构是有顺序的话,可以发现,发现强联通分量的顺序即为拓扑排序的倒序(这里将单独的一个节点也看做一个分量)。


硬核的总结:

算法的解析基本上到这里了,具体的细节可以直接到code中体会,还可以查一些其他的资料,这里不赘述了(懒,不想多写了)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值