强连通分量算法(2)

Tarjan's strongly connected components algorithm

Pku 2186,2553,1236,2762,都是可以用强连通分量算法来解决的。Kosaraju的算法在大部分情况下还是够用的,至少对于这四个题时间都还行。但是既然有更好的算法,那也学习一下吧。

The algorithm takes a directed graph as input, and produces a partition of the graph's vertices into the graph's strongly connected components. Every vertex of the graph appears in a single strongly connected component, even if it means a vertex appears in a strongly connected component by itself (as is the case with tree-like parts of the graph, as well as any vertex with no successor or no predecessor).

算法的输入是一个有向图,产生一个图的强连通分量顶点划分。图的每个顶点都会出现在一个强连通分量当中,尽管这意味着一个强连通分量可能只有一个顶点。

The basic idea of the algorithm is this: a depth-first search begins from an arbitrary start node (and subsequent depth-first searches are conducted on any nodes that have not yet been found). The search does not explore any node that has already been explored. The strongly connected components form the subtrees of the search tree, the roots of which are the roots of the strongly connected components.

基本思想是:从任意一个结点出发深搜(后续的深搜要在未访问过的结点上进行)。不能搜索已经探索过的结点。强连通分量形成了搜索树,他们的根就是强连通分量的根。

The nodes are placed on a stack in the order in which they are visited. When the search returns from a subtree, the nodes are taken from the stack and it is determined whether each node is the root of a strongly connected component. If a node is the root of a strongly connected component, then it and all of the nodes taken off before it form that strongly connected component.

被访问的结点按顺序放进栈中。当从一个搜索树返回时,判断该点是否是一个强连通分量的根。如果该点是一个强连通分量的根,那么从栈提出相应的点就是一个强连通分量了。

The crux of the algorithm comes in determining whether a node is the root of a strongly connected component. The concept of the "root" applies only to this algorithm (outside of the algorithm, a strongly connected component has no single "root" node). The root node is simply the first node of the strongly connected component which is encountered during the depth-first traversal. When a node is identified as the root node, once recursion on its successors has finished, all nodes on the stack from the root upwards form a complete strongly connected component.

算法的关键是判断一个结点是否是强连通分量的根。根这个概念仅用于这个算法中(其余的算法中,没有这个概念)。这个根结点是在深搜时碰到当前强连通分量的第一个结点。当一个结点被确定为一个根时,一旦后续子女递归完成,那么从栈中找到所有的相关结点就是一个完整的强连通分量。

To find the root, each node is given a depth search index v.index, which numbers the nodes consecutively in the order in which they are discovered. In addition, each node is assigned a value v.lowlink that is equal to the index of some node reachable from v, and always less than v.index, or equal to v.index if no other node is reachable from v. Therefore v is the root of a strongly connected component if and only if v.lowlink == v.index. The value v.lowlink is computed during the depth first search such that it is always known when needed.

为了找到这个根,每个结点都赋一个搜索序列号v.index, 这个号是搜索的先后顺序。另外,每个结点都赋一个值v.lowlink. 这个值等于从v能够到达的结点的序列号的最小值。总是小于v.index,或者等于v.index. 只有当v.lowlink==v.index的时候,才是强连通分量的根结点。V.lowlink的值是实时更新的。

algorithm tarjan is

  input: graph G = (V, E)

  output: set of strongly connected components (sets of vertices)

 

  index := 0

  S := empty

  for each v in V do

    if (v.index is undefined)

      strongconnect(v)

    end if

  repeat

 

  function strongconnect(v)

    // Set the depth index for v to the smallest unused index

    v.index := index

    v.lowlink := index

    index := index + 1

    S.push(v)

 

    // Consider successors of v

    for each (v, w) in E do

      if (w.index is undefined) then

        // Successor w has not yet been visited; recurse on it

        strongconnect(w)

        v.lowlink := min(v.lowlink, w.lowlink)

      else if (w is in S) then

        // Successor w is in stack S and hence in the current SCC

        v.lowlink := min(v.lowlink, w.index)

      end if

    repeat

 

    // If v is a root node, pop the stack and generate an SCC

    if (v.lowlink = v.index) then

      start a new strongly connected component

      repeat

        w := S.pop()

        add w to current strongly connected component

      until (w = v)

      output the current strongly connected component

    end if

  end function

关于算法正确性的解析:正如文中所说,判断每个结点是否为根节点。也许看完之后还是有些疑惑。为什么会这样? 假如v是一个强连通分量的一个点,深搜的时候第一个访问到它。从v能够访问的所有的点,都会有lowlink连接到v。那么就说明了这些点就是一个强连通分量。

举个例子:

 

对于这样一副图,从1开始深搜,到6的时候不能再往下搜了,此时如果6能够链到之前搜过的结点,那么就会出现scc.很显然这里没有,所以它的low就是自己的搜索序列号了。然后剔除。

返回5,5也没有链到之前的点,剔除

返回3,3可继续搜,到4了,4链到1,则low[4] = 1. 然后low[3]和low[1]都变成了1。然后返回到1,1可继续搜,搜到2,2链接到之前的点,则low[2] = DFN[4]. 在返回到1,则此时形成一个scc( 1 3 4 2).

posted @ 2011-02-27 23:01  AC2012  阅读(522)  评论(0编辑  收藏