割点与桥与缩点(tarjan)

割点:若删除该点,图不连通,则该点为割点
:若删除该边,图不连通,则该边为桥


如何求割点
Tarjan算法,一次dfs遍历:

  1. 对每个点,记录dfs序为dfn[],low值为low[](low初始值与dfn相同)
  2. 回溯时,如果回溯点的low值小于当前点low值,更新当前点low值
  3. 对于根节点,需要特判,其子节点>=2为割点

我们可以发现,low值记录的是:该点所在的强连通 能达到的最上高度
那么,对于一个点u,存在它的子节点和孙子节点v的low值,满足:
low[v]>=dfn[u]
即代表,v所在的强连通最上高度,都不超过u ,也就是说,如果v点想要达到u以上高度,必须经过u
此时,我们判定U为割点。


如何求
和上述一致,判定条件变为:
对点u,存在子节点满足,
low(v)>dfn(u),该边(u,v)为桥。
因为当low(v)=dfn(u)时,即表明两点间存在环路,不可能为桥;即只有小于情况下想要跳到u以上高度,一定经过(u,v)边。

void Tarjan(int u,int father){
    int son=0;
    low[u]=dfn[u]=++time;
    for(int i=first[u];~i;i=E[i].next){
        int v=E[i].v;
        if(v==father||v==key)continue;
        if(!dfn[v]){
            son++;
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);//更新low值
            if((low[v]>=dfn[u]&&u!=root)||(u==root&&son>1)){//割点
                    subnets[u]++;
                    flag=false;
                    return;
            }
        }
        else {
            low[u]=min(low[u],dfn[v]);
        }
    }
}

缩点:把同一个强连通分量内的点们缩成一点


如何缩点

  1. 对当次遍历的点u,进栈stack[]=u。标记点u已进入,instack[u]=1
  2. 遍历完u的所有边后,若low[u]=dfn[u] 。表示u和其子节点构成一个联通分量,缩点。
  3. 缩点数cnt++,从stack中出栈,栈中的点们都归与一个缩点,belong[]=cnt。标记点们出栈,instack[u]=0
void Tarjan(int u,int father){
    sta[++top]=u;//入栈
    instack[u]=1;
    low[u]=dfn[u]=++cnt;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(i==(father^1))continue;
        if(!dfn[v]){
            Tarjan(v,i);
            low[u]=min(low[u],low[v]);//更新low
        }
        else if(instack[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        key++;//缩点
        int v;
        do{
            v=sta[top--];//出栈
            instack[v]=0;
            belong[v]=key;
        }while(v!=u);
    }
}

你可能感兴趣的:(割点与桥与缩点(tarjan))