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

文章目录

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

这个思路也叫倍增 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(NLogN)

空间复杂度: O(NLogN)

单次查询 O(LogN)

Tag

Array BinaryLifting

你可能感兴趣的:(数据结构与算法,算法,链表,数据结构)