Python 程序检测有向图中的环

2024 年 8 月 29 日 | 4 分钟阅读

检测有向图中的是计算机科学中的一个经典问题。有几种算法可以解决这个问题,但最常见的一种是深度优先搜索 (DFS) 算法。

DFS 算法的基本思想是从一个顶点开始,沿着每条分支尽可能深地探索,然后再回溯。在此过程中,该算法将每个访问过的顶点标记为“已发现”,并在堆栈中跟踪当前路径上的顶点。如果它遇到一个已经被发现并且在堆栈中的顶点,那么图中就存在一个环。

用于使用 DFS 检测环的 Python 程序

输出

True
False

说明

第一个图有一个环(A->C->A),而第二个图没有。has_cycle 函数接受一个字典来表示图,其中每个键是一个节点,相应的值是其邻居列表。例如,图{'A': ['B', 'C'], 'B': ['C'], 'C': ['A']} 表示一个有边A->B, A->CC->A 的有向图。

该函数初始化两个集合:visited 用于跟踪已访问的顶点,stack 用于跟踪当前路径上的顶点。之后,它定义了一个嵌套的 DFS 函数,该函数接受一个节点并返回“True”(如果检测到环),否则返回“False”

DFS 函数首先将节点标记为已访问并将其添加到堆栈中。之后,它会递归地对尚未访问的节点的每个邻居调用自身。如果邻居已被访问并且在堆栈中,则存在环,并且函数返回“True”值。

如果未检测到当前节点的环,则将其从堆栈中移除并返回Falsehas_cycle 函数中的外部循环仅遍历图中的所有节点,并对任何未访问的节点调用DFS 函数。如果检测到环,则函数返回True;否则,返回False

时间复杂度

DFS 算法访问每个顶点和每条恰好一次,因此其时间复杂度为O(V + E),其中V顶点的数量,E 是图中的的数量。在最坏的情况下,该算法可能会访问图中的所有边,对于完全连接的图,这会产生O(V^2) 的时间复杂度。

空间复杂度

DFS 算法的空间复杂度取决于图的大小和递归堆栈的最大深度。在最坏的情况下,图是一条直线,堆栈可能包含所有顶点,空间复杂度为O(V)。但是,堆栈的最大深度通常远小于V,因此空间复杂度通常低得多。

在提供的实现中,我们使用了两个集合:visitedstack,两者最多可以存储V 个元素。此外,我们还有一个最大深度为V 的递归调用堆栈。因此,该算法的空间复杂度为O(V)

总之,DFS 算法是检测有向图中环的非常高效的算法。其时间复杂度为O(V + E),空间复杂度为O(V)

检测有向图中的环有不同的方法。以下是另外两种方法:

  • 拓扑排序与环检测

有向无环图 (DAG) 中,我们可以对节点进行拓扑排序,该排序会按顺序排列节点,使得所有边都从前面的节点指向后面的节点。如果图中存在环,则无法对其进行拓扑排序。因此,我们可以修改拓扑排序算法,在构建排序顺序的同时检测环。该方法的时间复杂度为O(V + E),其中V顶点的数量,E 是图中的的数量。

  • Tarjan 算法

Tarjan 算法是检测有向图中环的另一种流行算法。它基于图的强连通分量 (SCC) 的概念。SCC 是图中的一个节点子集,其中每个节点都可以从子集中的任何其他节点到达。Tarjan 算法在图上执行修改后的 DFS 并识别SCC。如果任何 SCC 包含多个节点,则图中存在环。该方法的时间复杂度为O(V + E),其中V顶点的数量,E 是图中的的数量。