求无向连通图的割点

求一个连通图的割点,割点的定义是,如果除去此节点和与其相关的边,图不再连通。

连通图的定义:如果图中任意两点都是连通的,那么图被称作连通图。如果此图是有向图,则称为强连通图(注意:需要双向都有路径)

割点:在无向连通图中,删除一个顶点v及其相连的边后,原图从一个连通分量变成了两个或多个连通分量,则称顶点v为割点,同时也称关节点 (Articulation Point)。多连通图没有割点,若在多连通图上至少删去k个顶点才能破坏图的连通性,则称此图为k连通图。貌似有向连通图没有割点这个说法。

连通分量:求无向图的连通分量的时候需要先找到割点。在求有向图的强连通分量的时候,所使用的方法与求无向连通图割点的做法很相似,所谓的Tarjan算法。


算法分析

经典的解法是建立DFS搜索树,有两类树节点可以成为割点:
  1. 对根节点v,若其有两棵或两棵以上的dfs子树,则该根结点为割点;
  2. 对非叶子节点v(非根节点),若其某个子树的节点均没有指向v的祖先节点的回边,则v为割点。
实现的时候,随着DFS的进行,需要维护2类信息,一是每个节点v的DFS序号(节点的访问次序),一是每个节点v及v的子树在DFS树中能追溯到的最早祖先节点(即DFS序号最小),将其记为v的最早祖先(至少能追溯到v的父节点)。

如果v的某一个子树无法回溯到v的祖先,则v是割点,所以在遍历v子树的过程中,v可能会被反复判断为割点,如果仅仅是求割点则需要去除重复记录, 如果是求连通分量则不需要。在遍历完节点v的所有子树及v自己的祖先后就可以求出v的最早祖先,进而结束v的处理,回到v的父节点。


// 输入图的邻接矩阵,节点编号0~n    
     void doDFS( const vector<vector< int> >& G, int v, int currDfs, bool isRoot,vector< int>& low,vector< int>& dfs,vector< int>& articulation);    
     void calcArticulation( const vector<vector< int> >& G,vector< int>& articulation)    
    {    
         int v =  0;    
         int currDfs= 0;    
        vector< int> low(G.size(),- 1), dfs(G.size(),- 1);    
        doDFS(G,v,currDfs, true,low,dfs,articulation);    
        
    }    
     // G:邻接矩阵, v当前节点,currdfs当前的dfs编号,isroot指示v是否是dfs树的根节点,    
    
// low记录所有节点的最早祖先,dfs记录所有节点的dfs序号,articulation存放求得的割点    
     void doDFS( const vector<vector< int> >& G, int v, int currDfs, bool isRoot,vector< int>& low,vector< int>& dfs,vector< int>& articulation)    
    {    
        dfs[v] = currDfs;    
        low[v] = currDfs;    
         bool isArticulation= false;    
         int child= 0// 记录节点v的dfs子树个数    
         for(size_t i= 0; i < G[v].size(); ++i)    
        {    
             if(G[v][i]== 0){ continue;}    
             int next = i;    
             if(dfs[next] == - 1){    
                child++;    
                doDFS(G,next,currDfs+ 1, false,low,dfs,articulation);    
                    
                 if(low[next] >= dfs[v]){ // 子树没有回溯到v的祖先,v是割点    
                    isArticulation =  true;     
                }    
                 else{ // 子树连通祖先,需要更新low[v]    
                    low[v] = low[next] < low[v] ? low[next]:low[v];    
                }    
            }    
             else{    
                 // 注意判断next小于low[v],因为low[v]可能被循环反复更新    
                low[v] = dfs[next] < low[v] ? dfs[next] : low[v];     
            }    
        }    
        
         if(isArticulation== true)    
        {    
             // 如果是DFS树根,还需要满足额外条件    
             if(isRoot== true) {    
                 if(child >  1){    
                    articulation.push_back(v);    
                }    
            }    
             else{    
                articulation.push_back(v);    
            }      
        }    
    }    
         static  void test( const vector<vector< int> >& G)    
    {    
        vector< int> articulation;    
        calcArticulation(G,articulation);    
         for(size_t i= 0; i < articulation.size();++i){    
            printf( " %d, ",articulation[i]);    
        }    
        printf( " \n ");    
    }    
     //   ---- 0    
    
//  |    / \    
    
//  |  1    4    
    
//  | / \    
    
//  2    3    
     static  void test1()    
    {    
         int t[ 5][ 5]={    
            { 0, 1, 1, 0, 1},    
            { 1, 0, 1, 1, 0},    
            { 1, 1, 0, 0, 0},    
            { 0, 1, 0, 0, 0},    
            { 1, 0, 0, 0, 0}    
        };    
            
        vector<vector< int> > G( 5);    
         for( int i= 0; i <  5 ;++i){    
            G[i].assign(t[i],t[i]+ 5);         
        }    
        test(G);    
    }    
     //        0    
    
//      /   \    
    
//     1     4    
    
//    / \    
    
//  2    3    
     static  void test2()    
    {    
         int t[ 5][ 5]={    
            { 0, 1, 0, 0, 1},    
            { 1, 0, 1, 1, 0},    
            { 0, 1, 0, 0, 0},    
            { 0, 1, 0, 0, 0},    
            { 1, 0, 0, 0, 0}    
        };    
            
        vector<vector< int> > G( 5);    
         for( int i= 0; i <  5 ;++i){    
            G[i].assign(t[i],t[i]+ 5);         
        }    
        test(G);    
    }    
     //       0    
    
//     /   \    
    
//    1     4    
    
//   / \    
    
//  2   3    
    
//       \    
    
//        5    
     static  void test3()    
    {    
         int t[ 6][ 6]={    
            { 0, 1, 0, 0, 1, 0},    
            { 1, 0, 1, 1, 0, 0},    
            { 0, 1, 0, 0, 0, 0},    
            { 0, 1, 0, 0, 0, 1},    
            { 1, 0, 0, 0, 0, 0},    
            { 0, 0, 0, 1, 0, 0}    
        };    
            
        vector<vector< int> > G( 6);    
         for( int i= 0; i <  6 ;++i){    
            G[i].assign(t[i],t[i]+ 6);         
        }    
        test(G);    
    }    
         //      0    
    
//     /  \    
    
//    1----3    
    
//   /    
    
//  2       
     static  void test4()    
    {    
         int t[ 4][ 4]={    
            { 0, 1, 0, 1},    
            { 1, 0, 1, 1},    
            { 0, 1, 0, 0},    
            { 1, 1, 0, 0}    
        };    
            
        vector<vector< int> > G( 4);    
         for( int i= 0; i <  4 ;++i){    
            G[i].assign(t[i],t[i]+ 4);         
        }    
        test(G);    
    }    
     //      0    
    
//     /    
    
//    1    
    
//   / \    
    
//  2   3    
     static  void test5()    
    {    
         int t[ 4][ 4]={    
            { 0, 1, 0, 0},    
            { 1, 0, 1, 1},    
            { 0, 1, 0, 0},    
            { 0, 1, 0, 0}    
        };    
            
        vector<vector< int> > G( 4);    
         for( int i= 0; i <  4 ;++i){    
            G[i].assign(t[i],t[i]+ 4);         
        }    
        test(G);    
    }    
     // 双连通图,没有割点    
    
//   -----0    
    
//  |   / |    
    
//  |  1  |    
    
//  | / \ |    
    
//  2     3    
     static  void test6()    
    {    
         int t[ 4][ 4]={    
            { 0, 1, 1, 1},    
            { 1, 0, 1, 1},    
            { 1, 1, 0, 0},    
            { 1, 1, 0, 0}    
        };    
            
        vector<vector< int> > G( 4);    
         for( int i= 0; i <  4 ;++i){    
            G[i].assign(t[i],t[i]+ 4);         
        }    
        test(G);    
    }    
     static  void test7()    
    {    
         int t[ 2][ 2]={    
            { 0, 1},    
            { 1, 0}            
        };    
            
        vector<vector< int> > G( 2);    
         for( int i= 0; i < 2 ;++i){    
            G[i].assign(t[i],t[i]+ 2);         
        }    
        test(G);    
    }    
     //      0    
    
//     / \    
    
//    1   4    
    
//   / \    
    
//  2   3    
    
//  |    \    
    
//   ---- 5    
     static  void test8()    
    {    
         int t[ 6][ 6]={    
            { 0, 1, 0, 0, 1, 0},    
            { 1, 0, 1, 1, 0, 1},    
            { 0, 1, 0, 0, 0, 0},    
            { 0, 1, 0, 0, 0, 1},    
            { 1, 0, 0, 0, 0, 0},    
            { 0, 1, 0, 1, 0, 0}    
        };    
            
        vector<vector< int> > G( 6);    
         for( int i= 0; i <  6 ;++i){    
            G[i].assign(t[i],t[i]+ 6);         
        }    
        test(G);    
    }    
     void testArticulation()    
    {    
        test1();    
        test2();    
        test3();    
        test4();    
        test5();    
        test6();    
        test7();    
        test8();    
    }

转载于:https://www.cnblogs.com/pop-lar/p/4287107.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值