在一个无向连通图中,如果任意去掉一个定点i及依附于i的所有边后得到的图仍然连通,
则称该图为“2-连通图”。否则,若得到多个连通分量,则该图不是双连通的,顶点i被称为
“割点 ”。
    简单的说,在双连通图中,任何一对顶点都至少存在两条路径可以互相到达。图的连通
性不会任何一个顶点的影响。这个性质具有许多重要的应用价值,例如现实中的通讯网络部署,
出于可靠性和容错性的考虑,在结构上应考虑多连通性,尽量避免割点的存在。这样就算一个
通信节点损毁,也不会对其他节点的通信造成影响。
    无向图的极大双连通子图被称为2-连通分量。一个无向图都具有一个或多个2-连通分量。
如果图本身是一个双连通图,那么它本身是自己唯一的2-连通分量。一个无向图具有2个或以
上2分量时,不难看出它的任意两个2分量最多只可能有一个公共顶点。因为如果有多于一个公
共顶点,就可以证明这两个2分量实际上可以组成一个更大的2分量,从而引起矛盾。进一步还
可以得出同一条边不可能处于多个二分量之中,因为一条边有两个顶点,如果一条边属于多个
二分量,则相当于两个顶点可以同时处于多个二分量之中,这与前述矛盾。
    综上,所有二分量实际上把图划分为了不相交的子集,连接不同二分量的边成为割边。
   
    可以利用深度优先搜索求2-连通分量。如果生成树的root是割点,当且仅当它有多于一个
子女。如果生成树的非根节点是割点,当且仅当它没有一个后代可以通过后向边回到它的祖先。
根据这些条件,我们定义一个点的时间戳为深度优先数,代表着顶点在深度优先搜索时被访问
的先后次序,记为D。如果D(i)<D(j),表明i点在serch过程中先于j点被访问。为了比较方便的
知道一个顶点是不是割点,对顶点定义参数low,low[i]为从i或其后代出发,通过后向边所能达
到的最小深度优先数。
    low[i]=min{ D(i) , min{low[j]|j为i的子女} , min{D(k)|(i,k)为后向边} }

    
// edge为无向图边表,n为顶点数, e总边数
     void  TwoCC( int  n, int  e) {
        
//D,low
        
//S 栈保存二分量边
        int *D=(int *)malloc(sizeof(int)*(n+1));
        
int *low=(int *)malloc(sizeof(int)*(n+1));
        
int *S=(int *)malloc(sizeof(int)*(2*e));
        memset(D,
0,sizeof(D));
        memset(low,
0,sizeof(low));
        
for(int i=0; i<n; i++){
            
// 若未访问则以此为起点寻找二分量
            if(D[i]==0)
                DFS(i,
-1,1,D,low,S,0);
        }

    }

   
    
//  从u做DFS,v是u之父,num为目前深度优先数
    
//  top --> S
     void  DFS( int  u, int  v, int  num, int  D[], int  low[], int  S[], int  top) {
        D[u]
=low[u]=num++;
        Edge
* l = edge[u];
       
        
// 搜索所有与i邻接之顶点
        while(l){
            
if( v!= l->dest && D[l->dest] < D[u] ){
                
// 连通分量边压栈
                S[top++]=u;
                S[top
++= l->dest;
            }

            
if( D[l->dest] == 0 ){
                DFS(l
->dest,u,num,D,low,S,top);
                low[u]
=( low[u] < low[l->dest] )? low[u] : low[l->dest];
           
                
// 无后向边,分量结束
                if( low[l->dest] == 0 ){
                    cout
<<"A 2-CC found:"<<endl;
                    
do{
                        
int x = S[--top];
                        
int y = S[--top];
                        cout
<<x<<" "<<y<<endl;
                        
if( x== u && y == l->dest ) break;
                    }
while(1)
                }

            }

            
else
                
if( l->dest != v ){
                    
// 如果l-dest已经被访问过,并且不等于父顶点v,证明有后向边更新low
                    low[u] = ( low[u] < D[l->dest] ) ? low[u] : D[l->dest];
                }

            l
=l->next;
        }

       
    }