• 有向图的强连通分量


    一.

    强连通分量:在有向图中,如果对于每一对a,b,且a != b,从a到b和从b到a都存在路径,则称该有向图时强连通图。有向图中的极大强连通子图称作有向图的强连通分量。

    二.

    深度优先搜索算法是求有向图的强连通分量的一个有效的方法。

    深度优先搜索算法:

     1 Boolean visited [ MAX ];     // 访问标志数组
     2 
     3 Status ( *VisitFunc ) ( int v );     // 函数变量
     4 
     5 void DFSTraverse ( Graph G, Status ( *Visit ) ( int v) ) {
     6 
     7 // 对图 G 作深度优先遍历
     8 
     9 VisitFunc = Visit;
    10 
    11 // 使用全局变量 VisitFunc,使 DFS 不必设函数指针参数
    12 
    13 for ( v = 0; v < G.vexnum; ++v )
    14 
    15 visited[v] = FALSE;     // 访问标志数组初始化
    16 
    17 for ( v = 0; v < G.vexnum; ++v )
    18 
    19 if ( ! visited[v] ) DFS ( G, v );     // 对尚未访问的顶点调用 DFS
    20 
    21 } // DFSTraverse
    22 
    23 void DFS ( Graph G, int v ) {
    24 
    25 // 从第 v 个顶点出发,递归地深度优先遍历图 G
    26 
    27 visited [v] = TRUE;
    28 
    29 VisitFunc (v);     // 访问第 v 个顶点
    30 
    31 for ( w = FirstAdjVex ( G, v ); w; w = NextAdjVex ( G, v, w ) )
    32 
    33 if ( ! vsited[w] ) DFS ( G, w );     // 对 v 的尚未访问的邻接顶点 w 递归调用 DFS
    34 
    35 } // DFS

    三.

    求有向图中的强连通分量思路

    1.Tarjan算法

    算法的基本思想如下:任选一结点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。

    结点按照被访问的顺序存入中。从搜索树的子树返回至一个结点时,检查该结点是否是某一强连通分量的根结点(见下)并将其从栈中删除。如果某结点是强连通分量的根,则在它之前出栈且还不属于其他强连通分量的结点构成了该结点所在的强连通分量。

    根结点的性质

    算法的关键在于如何判定某结点是否是强连通分量的根。注意“强连通分量的根”这一说法仅针对此算法,事实上强连通分量是没有特定的“根”的。在这里根结点指深度优先搜索时强连通分量中首个被访问的结点。

    为找到根结点,我们给每个结点v一个深度优先搜索标号v.index,表示它是第几个被访问的结点。此外,每个结点v还有一个值v.lowlink,表示从v出发经有向边可到达的所有结点中最小的index。显然v.lowlink总是不大于v.index,且当从v出发经有向边不能到达其他结点时,这两个值相等。v.lowlink在深度优先搜索的过程中求得,v是强连通分量的根当且仅当v.lowlink = v.index

    algorithm tarjan is
      input: 图 G = (V, E)
      output: 以所在的强连通分量划分的顶点集
    
      index := 0
      S := empty    // 置栈为空
      for each v in V do
        if (v.index is undefined)
          strongconnect(v)
        end if
    
      function strongconnect(v)
        // 将未使用的最小index值作为结点v的index
        v.index := index
        v.lowlink := index
        index := index + 1
        S.push(v)
    
        // 考虑v的后继结点
        for each (v, w) in E do
          if (w.index is undefined) then
            // 后继结点w未访问,递归调用
            strongconnect(w)
            v.lowlink := min(v.lowlink, w.lowlink)
          else if (w is in S) then
            // w已在栈S中,亦即在当前强连通分量中
            v.lowlink := min(v.lowlink, w.index)
          end if
    
        // 若v是根则出栈,并求得一个强连通分量
        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

    变量index是深度优先搜索的结点计数器。S是栈,初始为空,用于存储已经访问但未被判定属于任一强连通分量的结点。注意这并非一个一般深度优先搜索的栈,结点不是在以它为根的子树搜索完成后出栈,而是在整个强连通分量被找到时。

    最外层循环用于查找未访问的结点,以保证所有结点最终都会被访问。strongconnect进行一次深度优先搜索,并找到结点v的后继结点构成的子图中所有的强连通分量。

    当一个结点完成递归时,若它的lowlink仍等于index,那么它就是强连通分量的根。算法将在此结点之后入栈(包含此结点)且仍在栈中的结点出栈,并作为一个强连通分量输出。

  • 相关阅读:
    ~是什么意思 在C语言中,~0代表什么
    window中普通用户无法登录远程桌面
    服务器22端口被封锁的问题解决
    让hive的表注释和字段注释支持中文
    MySQL Workbench在archlinux中出现 Could not store password: The name org.freedesktop.secrets was not provided by any .service files的错误
    记使用talend从oracle抽取数据时,数字变为0的问题
    记mysql中时间相关的一个奇怪问题
    使用dbeaver查mysql的表会导致锁表的问题
    oracle中实现某个用户truncate 其它用户下的表
    Oracle中找出用户的上次登录时间
  • 原文地址:https://www.cnblogs.com/churi/p/3677285.html
Copyright © 2020-2023  润新知