【算法】Kth Ancestor of a Tree Node 树节点的第 K 个祖先

Kth Ancestor of a Tree Node 树节点的第 K 个祖先

问题描述:

给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。

树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点

k,n范围[1,50000]

parent[0] == -1 表示编号为 0 的节点是根节点

最多调用次数50000

分析

树的结构是通过数组给出,parent[i]表示的是节点的父节点,而且树可能是一个N叉树,每个节点都有一个唯一编号,要求从指定编号的节点出发直到根节点的路径上第k个祖先节点。
最通常可以想到的就是构建一个非线性结构树,从提供的数组可以方便的找到每个节点的父节点。

方向没问题,但是以要求的数据规模,这个思路是无法承受的

如果是从叶子出发向上寻找,最差的情况下,树退化成为链表,找一次k,就要遍历k个父节点,如果K很大的情况下,时间复杂度也是很庞大的。

因此就要有些手段加速寻找。

因为已经得到了pa数组,要查询节点i的第k个祖先,第一个祖先就是pa[i],第二个祖先为pa[pa[i]],第二个祖先为pa[pa[pa[i]]],…。
i − > p a [ i ] − > p a [ p a [ i ] ] − > p a [ p a [ p a [ i ] ] ] − > … i->pa[i]->pa[pa[i]]->pa[pa[pa[i]]]->\dots i>pa[i]>pa[pa[i]]>pa[pa[pa[i]]]>

既然可以直接找到父节点,那么在 O ( 1 ) O(1) O(1)时间内找到父节点的父节点,也是可以的,前提是需要预处理。
定义 f [ i ] [ j ] f[i][j] f[i][j]表示 节点i的第 2 j 2^j 2j个祖先节点的编号, f [ i ] [ 0 ] f[i][0] f[i][0]就是i的第一个祖先节点即父节点 f [ i ] [ 1 ] f[i][1] f[i][1]为第二个祖先节点即父节点的父节点
如果当第 2 j 2^j 2j个祖先节点不存在,那么就是-1.
因此查找i的第k个祖先节点,就是把k用二进制表示,对 i i i预处理出它的每一个 2 j 2^j 2j祖先编号。
由于n的最大规模是50000,所以j最大开到16基本可以覆盖。

BTW,这个思路也叫倍增 Binary Lifting

代码

int[][] f;
    int maxPow; 
    public TreeAncestor(int n, int[] parent) {
        // log_base_2(n)
        maxPow = (int) (Math.log(n) / Math.log(2)) + 1;
        f = new int[n][maxPow];
        for(int i=0;i<n;i++){
            f[i][0] = parent[i];
        }        
        for (int i = 0; i<n ; i++) {
            for (int j = 1; j < maxPow; j++) {
                int p = f[i][j-1]; 
                f[i][j] = p==-1?-1:f[p][j-1];
            }
        }
    }

    public int getKthAncestor(int node, int k) {
        int pos =0, res = node;
        while(k>0&& res!=-1){
            if(pos>=f[res].length) return -1;
            if((k&1)!=0){
                res = f[res][pos];
            }
            k>>=1;pos++;
        }
        return res; 
    }

时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN)

空间复杂度: O ( N L o g N ) O(NLogN) O(NLogN)

单次查询 O ( L o g N ) O(LogN) O(LogN)

Tag

Array BinaryLifting

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric.Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值