zoukankan      html  css  js  c++  java
  • 有向图的强连通算法 -- tarjan算法


    (绘图什么真辛苦)

    强连通分量:


    在有向图 G 中。若两个顶点相互可达,则称两个顶点强连通(strongly connected)。

    假设有向图G的每两个顶点都强连通,称G是一个强连通图非强连通图有向图的极大强连通子图。称为强连通分量(strongly connected components)。

    比方上面这幅图( a, b, e ), ( d, c, h ), ( f, g ) 分别为三个 SCC。


    tarjan算法伪代码:

    该算法由 Robert Tarjan 发明,原论文:Tarjan1972

    时间复杂度是深搜的时间复杂度 O( N + E )。


    BEGIN
        INTERGER i;
        PROCEDURE STRONGCONNECT(v);
            BEGIN
                LOWLINK(v):= NUMBER(v):= i := i + 1;
                put v on stack of points;
                FOR w in the adjacency list of v DO
                    BEGIN
                        IF w is not yet numbered THEN
                            BEGIN comment( v, w ) is a tree arc;
                                STRONGCONNECT(w);
                                LOWLINK(V) := min( LOWLINK(V),
                                                   LOWLINK(W));
                            END;
                        ELSE IF NUMBER(W) < NUMBER(V) DO
                            BEGIN comment( v, w ) is a frond or cross-link;
                                if w is on stack of points THEN
                                    LOWLINK(v) := min( LOWLINK(v),
                                                       NUMBER(w));
                            END;
                    END;
                    
                if( LOWLINK(v) = NUMBER(v) ) THEN
                    BEGIN comment v is the root of a compont;
                        start new strongly connected compont;
                        WHILE w on top of point stack satisfies
                            NUMBER(w) >= NUMBER(v) DO
                            BEGIN
                                    delete w from point stack and put w
                                    in current component;
                            END;
                    END;
            END;
        i := 0;
        empty stack of points;
        FOR w a vertex IF w is not yet numbered THEN STRONGCONNECT(w);
    END


    tarjan算法的运行动态图:

    1.建图。每一个顶点有三个域。第一个是顶点名。第二个空格是发现该点的时间戳 DFN ,第三个空格是该点可以追溯到的最早的栈中节点的次序号 LOW。



    2. 深搜,比方搜索路径为 a --> b --> c --> g --> f。 沿途记下自身的 DFN 和 LOW



    3.达到 f 点后,f 点仅仅有一个可达顶点 g, 但 g 不是 f 的后继顶点。且 g 在栈中,则更新 f 的 LOW 变为 g 的发现时间。



    4.这时候回到 g 点。可是 f 点还得再栈中,仅仅有发现时间 DFN 与 LOW 同样的顶点才干从栈中弹出(和压在上面的节点一起弹出构成SCC)




    5.这时候 g 点的 DFN == LOW



    6.于是将 f 和 g 都弹出



    7.如虚线所看到的,他们构成了一个SCC



    8.以下就是一样的了,( 图画的好辛苦 )


         

    =========================================================================================


        

    ========================================================================================

        

    =========================================================================================

        

    ==========================================================================================

        

    ==========================================================================================



    python代码:

    def strongly_connected_components( graph ):
        
        dfn_count = [0]
        result = []
        stack = []
        low = {}
        dfn = {}
        
        def stronglyconnected( node ):
            
            dfn[node] = dfn_count[0]
            low[node] = dfn_count[0]
            dfn_count[0] += 1
            stack.append( node )
    
            if node not in graph:
                successors = []
            else:
                successors = graph[node]
                
            for successor in successors:
                if successor not in dfn:
                    stronglyconnected( successor )
                    low[node] = min( low[successor], low[node] )
                elif successor in stack:
                    low[node] = min( low[node], dfn[successor] )
    
            if low[node] == dfn[node]:
                li = []
                item = None
                while True:
                    item = stack.pop()
                    li.append( item )
                    if item == node: break
                result.append( li )
            
        for node in graph:
            if node not in low:
                stronglyconnected( node )
    
        return result
    
    if __name__ == '__main__':
        graph = {
            'a': [ 'b' ],
            'b': [ 'c', 'e', 'f' ],
            'c': [ 'g', 'd' ],
            'd': [ 'c', 'h' ],
            'e': [ 'a', 'f' ],
            'f': [ 'g' ],
            'g': [ 'f' ],
            'h': [ 'g', 'd' ]
        }
        print strongly_connected_components( graph )
    

    执行结果:




    C++代码:

    #include <iostream>
    #include <vector>
    #include <string.h>
    using namespace std;
    
    #define MAX_SIZE 100
    
    bool Graph[MAX_SIZE][MAX_SIZE];
    
    int Stack[MAX_SIZE];
    bool nodeIsInStack[MAX_SIZE];
    int stack_pointer = 0;
    
    int Lows[MAX_SIZE];
    int Dfns[MAX_SIZE];
    
    int node_num = 0;   // 图中节点得到个数
    int find_time = 0;  // 每一个节点的发现时间
    
    int scc_num = 0;    // 记录 scc 的个数
    
    
    void find_scc( int start_node ){
    
        find_time++;
        Lows[start_node] = Dfns[start_node] = find_time;
    
        stack_pointer++;
        Stack[stack_pointer] = start_node;
        nodeIsInStack[start_node] = true;
    
        for( int end_node = 1; end_node <= node_num; ++end_node ){
            //若start_node和end_ndoe之间存在边
            if( Graph[start_node][end_node] ){
                //若是end_node尚未被訪问
                if( Dfns[end_node] == 0 ){
                    find_scc( end_node );
                    Lows[start_node] = min( Lows[start_node], Lows[end_node] );
                }
                //若end_node在栈中。也就是start_node -> end_node是返祖边
                else if( nodeIsInStack[end_node] ){
                    Lows[start_node] = min( Lows[start_node], Dfns[end_node] );
                }
            }
        }
    
        //若是start_node的时间戳与Lows相等在构成SCC
        if( Dfns[start_node] == Lows[start_node] ){
    
            scc_num++;
            cout << "scc_num: " << scc_num << endl;
    
            int pop_node_index = Stack[stack_pointer];
            stack_pointer--;
            nodeIsInStack[pop_node_index] = false;
    
            cout << pop_node_index << " ";
    
            while( start_node != pop_node_index ){
    
                pop_node_index = Stack[stack_pointer];
                nodeIsInStack[pop_node_index] = false;
                stack_pointer--;
                cout << pop_node_index << " ";
            }
            cout << endl;
        }
    }
    
    void init_values(){
        memset( Graph, false, sizeof( Graph ) );
        memset( Stack, 0, sizeof( Stack ) );
        memset( nodeIsInStack, false, sizeof( nodeIsInStack ) );
        memset( Lows, 0, sizeof( Lows ) );
        memset( Dfns, 0, sizeof( Dfns ) );
    }
    
    int main(){
        init_values();
        // 初始化图
        //这里用数字取代节点的名字
        int start_node, end_node;
        cin >> node_num;
    
        while( true ){
            cin >> start_node >> end_node;
            //起始点终止点都为 0 的时候结束
            if( start_node == 0 && end_node == 0 )
                break;
            Graph[start_node][end_node] = true;
        }
    
        for( int start_node = 1; start_node <= node_num; ++start_node ){
            //该节点尚未被訪问到
            if( Dfns[start_node] == 0 ){
                find_scc( start_node );
            }
        }
    
    }
    


  • 相关阅读:
    现在的代码,贴一下
    2014年七月写过的代码,现在看来,还有待改进呀
    第一次做技术论坛发博文,不知道说点啥
    04-树6. Huffman Codes--优先队列(堆)在哈夫曼树与哈夫曼编码上的应用
    04-树5. File Transfer--并查集
    04-树4. Root of AVL Tree-平衡查找树AVL树的实现
    03-树3. Tree Traversals Again (25)将先序遍历和中序遍历转为后序遍历
    03-树2. List Leaves (25) 二叉树的层序遍历
    二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历
    最大子序列和问题之算法优化
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7396057.html
Copyright © 2011-2022 走看看