zoukankan      html  css  js  c++  java
  • LCA

    lca指的是树上两点的最近公共祖先,也就是说,在这两点的所有祖先中深度最大的就是它们的最近公共祖先。

    求解lca的方法一般情况下有三种:向上标记法,树上倍增法,tarjan

    此处只介绍前两种方法(因为不会tarjan


     

    • 向上标记法

        假定一棵树有8个节点,如图所示, lca(4,7)=1

      

          例如求4处有一只小兔子A,7处也有一只小兔子B,它们都只能顺着这棵树向上跳

        现在想要知道它们最近可以在什么地方见面

        解决这个问题,其实就是求lca(4,7)

        我们让小兔子A从4向上跳到根节点,并标记所有经过的节点

        同样的,让小兔子B从7向上走到根节点,第一次遇到的被标记的节点就是4和7的最近公共祖先

        时间复杂度最坏为O(n)

      下面重点介绍树上倍增法(因为常用)

    • 树上倍增法

        仍然是上图,求同样的问题

        可以发现4的深度比7的深度深

        那么让小兔子A从4向上跳,直到和小兔子B所在的节点7同一深度时停下来

        此时如果两只小兔子已经会面,那么此时它们所在的点就是lca(4,7)的值

        如果没有会面呢?

        那么就让两只小兔子一起向上跳,直到它们会面为止

        但是有一个问题,如果小兔子A所在的树链特别的长,那它岂不是要跳好久才能和小兔子B到同一高度?

        所以聪明的小兔子就想出了一个办法:每次按2的i次幂跳

        这就用到了倍增

        这样子在数据最坏的情况下两只小兔子也可以很快见面啦

        不妨设f[x][i]表示x向上的第2i个节点

        对于初始化,显然f[x][0]也就表示x的父节点

        对于范围内的任意i,f[x][i]=f[ f[x][i-1] ][i-1]

        除了f数组之外,我们还需要一个d数组记录每个点的深度

        具体如何求解lca(x,y)呢?步骤如下:

          首先找出深度更深的点x(如果y比x深度深就交换)

          然后让x向上倍增直到与y同一深度

          如果此时x与y是同一点,那么就返回x所在位置(返回y所在位置也无所谓)

          如果不是同一点就一起向上倍增,直到是同一点就返回此时点所在的位置

        如果还是有疑问,那么就请参考以下程序理解吧

        另外,我用的是vector存图,当然也可以用链式前向星

    vector<int> g[maxn];
    void pre(int u,int fa){//初始化 
        d[u]=d[fa]+1;//求深度 
        for(int i=1;i<=15;i++){//i的范围根据题的数据范围而定 
            f[u][i]=f[f[u][i-1]][i-1];
        }
        int sz=g[u].size();//表示u所连接的边数 
        for(int i=0;i<sz;i++){
            if(g[u][i]!=fa){//g[u][i]表示以u为起点所连接的第i条边的终点 
                f[g[u][i]][0]=u;
                pre(g[u][i],u);
            }
        }
    }
    int lca(int x,int y){
        if(d[x]<d[y]){//让x为深度最深的点 
            swap(x,y);
        }
        for(int i=15;i>=0;i--){//跳到同一深度 
            if(d[f[x][i]]>=d[y]){
                x=f[x][i];
            }
            if(x==y){//如果此时是同一个点就输出 
                return x;
            }
        }
        for(int i=15;i>=0;i--){//让x,y同时向上跳 
            if(d[f[x][i]]!=d[f[y][i]]){//如果不是同一点就更新x,y的值 
                x=f[x][i];             //如果是同一点那么x的父节点就是答案 
                y=f[y][i];
            }
        }
        return f[x][0];//答案就是x的父节点 
    }

    就这么多啦 欢迎指正

  • 相关阅读:
    浅谈管道模型(Pipeline)
    经常使用的webservice接口
    2012年终总结
    【Linux】linux经常使用基本命令
    php:兄弟连之面向对象版图形计算器1
    学习selenium所须要具备的技术
    Sftp和ftp 差别、工作原理等(汇总ing)
    对数据分布进行可视化的方法
    FusionCharts简单教程(一)---建立第一个FusionCharts图形
    闭包和柯里化
  • 原文地址:https://www.cnblogs.com/duojiaming/p/11298826.html
Copyright © 2011-2022 走看看