Loading docs/graph/scc.md +8 −9 Original line number Diff line number Diff line Loading @@ -31,10 +31,6 @@ Tarjan 发明了很多算法结构。光 Tarjan 算法就有很多,比如求 3. 横叉边(cross edge):红色边,它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点 **并不是** 当前结点的祖先时形成的。 4. 前向边(forward edge):蓝色边,它是在搜索的时候遇到子树中的结点的时候形成的。 一个结点的子树内结点的 DFN 都大于该结点的 DFN。 从根开始的一条路径上的 DFN 严格递增。 我们考虑 DFS 生成树与强连通分量之间的关系。 如果结点 $u$ 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 $u$ 为根的子树中。 $u$ 被称为这个强连通分量的根。 Loading @@ -44,15 +40,18 @@ Tarjan 发明了很多算法结构。光 Tarjan 算法就有很多,比如求 ### Tarjan 算法求强连通分量 在 Tarjan 算法中为每个结点 $u$ 维护了以下几个变量: 1\. $DFN[u]$ :深度优先搜索遍历时结点 $u$ 被搜索的次序。 2\. $LOW[u]$ :设以 $u$ 为根的子树为 $Subtree(u)$ 。 $LOW[u]$ 定义为以下结点的 $DFN$ 的最小值: $Subtree(u)$ 中的结点;从 $Subtree(u)$ 通过一条不在搜索树上的边能到达的结点。 显然,按照 DFS 搜索树的递归顺序, $LOW[u]$ 是单调递增的。 1. $DFN[u]$ :深度优先搜索遍历时结点 $u$ 被搜索的次序。 2. $LOW[u]$ :设以 $u$ 为根的子树为 $Subtree(u)$ 。 $LOW[u]$ 定义为以下结点的 $DFN$ 的最小值: $Subtree(u)$ 中的结点;从 $Subtree(u)$ 通过一条不在搜索树上的边能到达的结点。 一个结点的子树内结点的 DFN 都大于该结点的 DFN。 从根开始的一条路径上的 DFN 严格递增,LOW 严格非降。 按照深度优先搜索算法搜索的次序对图中所有的结点进行搜索。在搜索过程中,对于结点 $u$ 和与其相邻的结点 $v$ (v 不是 u 的父节点)考虑 3 种情况: 1. $v$ 未被访问:继续对 $v$ 进行深度搜索。在回溯过程中,用 $LOW[v]$ 更新 $LOW[u]$ 。因为存在从 $u$ 到 $v$ 的直接路径,所以 $v$ 能够回溯到的已经在栈中的结点, $u$ 也一定能够回溯到。 2. $v$ 被访问过,已经在栈中:即已经被访问过,根据 $LOW$ 值的定义(能够回溯到的最早的已经在栈中的结点),则用 $DFN[v]$ 更新 $LOW[u]$ . 2. $v$ 被访问过,已经在栈中:即已经被访问过,根据 $LOW$ 值的定义(能够回溯到的最早的已经在栈中的结点),则用 $DFN[v]$ 更新 $LOW[u]$ 。 3. $v$ 被访问过,已不在在栈中:说明 $v$ 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。 将上述算法写成伪代码: Loading Loading @@ -105,7 +104,7 @@ Kosaraju 算法依靠两次简单的 DFS 实现。 第二次 DFS,对于反向后的图,以标号最大的顶点作为起点开始 DFS。这样遍历到的顶点集合就是一个强连通分量。对于所有未访问过的结点,选取标号最大的,重复上述过程。 两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为 $O(n+m)$ 两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为 $O(n+m)$ 。 ### 实现 Loading Loading
docs/graph/scc.md +8 −9 Original line number Diff line number Diff line Loading @@ -31,10 +31,6 @@ Tarjan 发明了很多算法结构。光 Tarjan 算法就有很多,比如求 3. 横叉边(cross edge):红色边,它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点 **并不是** 当前结点的祖先时形成的。 4. 前向边(forward edge):蓝色边,它是在搜索的时候遇到子树中的结点的时候形成的。 一个结点的子树内结点的 DFN 都大于该结点的 DFN。 从根开始的一条路径上的 DFN 严格递增。 我们考虑 DFS 生成树与强连通分量之间的关系。 如果结点 $u$ 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 $u$ 为根的子树中。 $u$ 被称为这个强连通分量的根。 Loading @@ -44,15 +40,18 @@ Tarjan 发明了很多算法结构。光 Tarjan 算法就有很多,比如求 ### Tarjan 算法求强连通分量 在 Tarjan 算法中为每个结点 $u$ 维护了以下几个变量: 1\. $DFN[u]$ :深度优先搜索遍历时结点 $u$ 被搜索的次序。 2\. $LOW[u]$ :设以 $u$ 为根的子树为 $Subtree(u)$ 。 $LOW[u]$ 定义为以下结点的 $DFN$ 的最小值: $Subtree(u)$ 中的结点;从 $Subtree(u)$ 通过一条不在搜索树上的边能到达的结点。 显然,按照 DFS 搜索树的递归顺序, $LOW[u]$ 是单调递增的。 1. $DFN[u]$ :深度优先搜索遍历时结点 $u$ 被搜索的次序。 2. $LOW[u]$ :设以 $u$ 为根的子树为 $Subtree(u)$ 。 $LOW[u]$ 定义为以下结点的 $DFN$ 的最小值: $Subtree(u)$ 中的结点;从 $Subtree(u)$ 通过一条不在搜索树上的边能到达的结点。 一个结点的子树内结点的 DFN 都大于该结点的 DFN。 从根开始的一条路径上的 DFN 严格递增,LOW 严格非降。 按照深度优先搜索算法搜索的次序对图中所有的结点进行搜索。在搜索过程中,对于结点 $u$ 和与其相邻的结点 $v$ (v 不是 u 的父节点)考虑 3 种情况: 1. $v$ 未被访问:继续对 $v$ 进行深度搜索。在回溯过程中,用 $LOW[v]$ 更新 $LOW[u]$ 。因为存在从 $u$ 到 $v$ 的直接路径,所以 $v$ 能够回溯到的已经在栈中的结点, $u$ 也一定能够回溯到。 2. $v$ 被访问过,已经在栈中:即已经被访问过,根据 $LOW$ 值的定义(能够回溯到的最早的已经在栈中的结点),则用 $DFN[v]$ 更新 $LOW[u]$ . 2. $v$ 被访问过,已经在栈中:即已经被访问过,根据 $LOW$ 值的定义(能够回溯到的最早的已经在栈中的结点),则用 $DFN[v]$ 更新 $LOW[u]$ 。 3. $v$ 被访问过,已不在在栈中:说明 $v$ 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。 将上述算法写成伪代码: Loading Loading @@ -105,7 +104,7 @@ Kosaraju 算法依靠两次简单的 DFS 实现。 第二次 DFS,对于反向后的图,以标号最大的顶点作为起点开始 DFS。这样遍历到的顶点集合就是一个强连通分量。对于所有未访问过的结点,选取标号最大的,重复上述过程。 两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为 $O(n+m)$ 两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为 $O(n+m)$ 。 ### 实现 Loading