关于tarjan算法的一些理解(割点割边)

首先介绍以下tarjan算法:

---------------------------------------------tarjan算法-----------------------------------------------------------

tarjan算法思想:

tarjan算法在离线求LCA,强连通分量,割边,割点,点双连通分量,边双连通分量很有用

tarjan算法中主要维护的是两个数组,dfn[i]数组存的是深搜各个点的时间戳,low[i]记录的是i能够直接通过其深搜子树里的点间接达到的时间戳最小的点。

然后深搜过程中不断给点加时间戳,对于当前点u,点v与u相连,如果点v不是点u的父亲,如果点v尚未标记,那么先对点v及其子树加时间戳,然后回溯的时候利用除了点u父亲之外的点更新low[u],如果当前儿子已经标记了时间戳,那么证明它之前已经被访问过,那么利用dfn[v]更新low[u]

tarjan算法就是在对所有加时间戳的过程中维护这两个数组。

那么我们结合求割点和割边来具体讲解tarjan算法的实际应用:

----------------------------------------tarjan算法求割点--------------------------------------------------------

割点:在一个无向连通图中,如果去掉这个点以及连向它的边,那么这个图不再连通,那么这个点就是割点

那么我们根据割点的性质,在深搜树中,如果对于某个点u,与它相连的点v(v不是u的父亲),那么如果low[v]>=dfn[u],那么也就是以v为根的深搜子树中的点所连接的点没有已经标记时间戳的,也就是以v为根的子树是封闭的,那么一旦去掉点u,这棵子树中的点就称为了一个新的连通分量,那么点u就是割点了

void dfs ( int u , int pre )  
{  
    int i,j,v;  
    dfn[u] = low[u] = ++ts;  
    stk[++s] = u;  
    for ( i = head[u] ; i != -1 ; i = e[i].next )  
    {  
        v = e[i].v;  
        if ( !dfn[v] )  
        {  
            dfs ( v ,u );  
            low[u] = min ( low[u],low[v]);  
            //求割点,利用割点划分出点双连通分量  
            if ( dfn[u] <= low[v] )  
            {  
                memset ( in , 0 , sizeof (in));  
                in[u] = 1;  
                while ( stk[s] != v )  
                {  
                    in[stk[s]]=1;  
                    s--;  
                }  
                in[v] = 1;  
                s--;  
                memset (color,-1,sizeof(color) );  
                if ( paint(u , 1 , -1 ) )  
                {  
                    for (j =1 ; j <= n ; j++ )  
                        if ( in[j] == 1 ) flag[j]=true;  
                }  
            }  
        }  
        else if ( v != pre ) low[u] = min ( low[u] , dfn[v] );  
    }  
}  

-------------------------------------------割边--------------------------------------------------------------------------

割边:如果去掉某一条边之后,联通分量的数目变多,那么这个点就是割边

还是利用low[u]数组和dfn[u]数组来判断割边,对于一条边如果它是割边的话,那么low[v] > dfn[u] ,也就是以v为根的子树是封闭的,只要去掉u,v连接的这条边,就会增加联通分量的数量

void tarjan ( int u , int p )  
{  
    dfn[u] = low[u] = ++times;  
    for ( int i = head[u] ; ~i ; i = e[i].next )  
    {  
        int v = e[i].v;  
        if ( !dfn[v] )  
        {  
            tarjan(v,u);  
            low[u] = min ( low[u] , low[v] );  
            if ( low[v] > dfn[u] && !e[i].tag )  
                ans.push_back ( e[i].id );  
        }  
        else if ( v != p )  
            low[u] = min ( low[u] , dfn[v] );  
    }  
}  



你可能感兴趣的:(C++,图论,Tarjan,割点,割边)