• Tarjan算法详解理解集合


    【功能】

        Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

    【算法思想】

        用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)

        程序开始时,time初始化为0,在dfs遍历到v时,low[v]=dfn[v]=time++,

    v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果 k没有被访问过那么先dfs遍历k,low[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。

    【大概的证明】

     1.  在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点:

        因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。

     

    2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于 自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。

    【时间复杂度】

         因为所有的点都刚好进过一次栈,所有的边都访问的过一次,所以时间复杂度为O(n+m)

    【可看证明】

    若存在边<i, j>且遍历到它的时候j在栈中,那么i和j可能存在三种关系:
    (1)i是j的祖先;
    (2)j是i的祖先;
    (3)i和j无前后关系。
    对于情况(1),必有dfn[j]>dfn[i],因此不必考虑;
    对于情况(2),<i, j>是逆向边,显然i、j处于同一个强连通分支;
    对于情况(3):<i, j>是横叉边。显然i、j必然在同一棵搜索树中(因为搜索树的根结点肯定满足low=dfn),设p=LCA(i, j),由于从p到j的路径上木有low=dfn的结点(否则j已经出栈了),所以j必然可以到达p,又因为p可以到达i,所以j也可以到达i,又因为存在边<i, j>,所以i、j处于同一个强连通分支,这样就需要在计算low[i]的时候把dfn[j]考虑进去,而不能让i及其所有后代成为一个强连通分支。

    【外援】https://www.byvoid.com/blog/scc-tarjan

  • 相关阅读:
    AOP面向切面编程相关核心概念
    什么是AOP?
    vue-koa-mongodb管理系统
    js算法(个人整理_彦超)
    前端面试基础总结(个人整理_彦超)
    HTTP 知识点总结(个人整理_彦超)
    前端手写代码整理(个人整理_彦超)
    小程序框架
    nvm 的安装与使用
    three.js 火焰效果
  • 原文地址:https://www.cnblogs.com/you-well-day-fine/p/4058515.html
Copyright © 2020-2023  润新知