zoukankan      html  css  js  c++  java
  • tarjan 学习笔记

    无向图的割顶和桥

    • low[i] 表示 i号节点能访问到最早节点的编号
    • dfn[i] 表示 i号节点在dfs序中的编号
    void tarjan(int u,int fa){
        int v;
        int child = 0,k = 0;
        dfn[u] = low[u] = ++dfs_clock;
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            v = edge[i].to;
            if(v == fa && !k){  // 处理重边 第一次访问到父亲则不访问
                k++;
                continue;
            }
            if(!dfn[v]){        // v是u的儿子
                child++;
                tarjan(v,u);
                low[u] = min(low[u],low[v]); // 儿子能访问到,父亲也能访问到
                if(low[v] > dfn[u]){    // 儿子不能访问到父亲前面的点
                    // is_cut[i] = true;    // i号边为割边
                }
                if(low[v] >= dfn[u] && fa!=-1){ // 非树根且儿子不能访问他前面的点
                    iscutpoint[u] = true;
                }
            }else{              // v已经被访问过, 即u可以绕过父亲来访问更靠前的点
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(fa == -1 && child>1 ){   // 有超过两个儿子的树根一定是割点
            iscutpoint[u] = true;
        }
    }
    

    非根

    如图 2的儿子连回了他的父亲1的父亲,导致1不能成为割点. 而若是2的儿子连回了1,则1仍然可以作为割点
    对于桥,若2的儿子既不能连回1也不能连回1的父亲,即图中蓝色的边不存在,则1-2这条边即为割边

    树根

    不同子树之间只能通过树根相连,所以只要超过两颗子树树根就是割点

    对于根的子树互相相连,可以认为只有一颗子树,其余与根相连的边均为反向边即可.

    • 割边:(low[v] > dfn[u])
    • 割点:(low[v] >= dfn[u]) || ((is_root) && (son_cnt>1))

    边-双联通分量

    • 定义: 任意两点存在至少两条"边不重复"的路径 => 所有的边均不是桥
    • 求法: 第一遍dfs找出所有的桥,第二遍dfs不经过桥,所经过的点均在同一双联通分量中
      // 或者同求强联通分量的方法,low[u] == dfn[u] 即进行缩点(不加u)
    void tarjan(int u,int fa){
        dfn[u] = low[u] = ++stemp;
        s.push(u); in[u] = true;
        int son = 0,k = 1;
        for(int i=head[u];i;i=e[i].nxt){
            int v = e[i].to;
            if(v==fa && k){k = 0; continue;}    //   处理重边
            if(!dfn[v]){
                tarjan(v,u);
                low[u] = min(low[u],low[v]);
            }else if(in[v]){
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(low[u] == dfn[u]){
            int v; ++cnt_block;
            do{
                v = s.top();    s.pop();
                belong[v] = cnt_block;
            }while(v!=u);
        }
    }
    

    点-双联通分量

    • 定义: 任意两点至少存在两条"点不重复"的路径 => 分量内部无割点
    • 求法: tarjan过程中遇到low[v] >= dfn[u] 将栈中所有点弹入一个联通分量(加入u但不将u弹出,因为u可能属于多个连通分量)
    void tarjan(int u,int fa){
        dfn[u] = low[u] = ++stemp;
        s.push(u);
        int son = 0,k = 1;
        for(int i=head[u];i;i=e[i].nxt){
            int v = e[i].to;
            if(v==fa && k){k = 0; continue;}    //   处理重边
            if(!dfn[v]){
                tarjan(v,u);
                low[u] = min(low[u],low[v]);
                if(low[v] >= dfn[u]){
                    if(fa != -1)    cut[u] = 1;
                    blocks[++cnt_block].clear();
                    blocks[cnt_block].push_back(u); // 加入u
                    int now;
                    do{
                        now = s.top();  s.pop();
                        blocks[cnt_block].push_back(now);
                    }while(now != v);               //栈弹到v,并加入双联通分量中
                }
            }else{
                low[u] = min(low[u],dfn[v]);
            }
        }
        if(fa==-1 && son>1) cut[u] = 1;
    }
    
    
  • 相关阅读:
    Spring AOP 实现原理
    Spring Aop实现方式总结
    Spring Aop重要概念介绍及应用实例结合分析
    Spring Aop
    常见的排序算法
    MINA2.0原理
    Java和Tomcat类加载机制
    Java 8 新特性概述
    Java类加载机制深度分析
    jetty之建立多Connector
  • 原文地址:https://www.cnblogs.com/xxrlz/p/11372103.html
Copyright © 2011-2022 走看看