LC 1483 [H] Kth Ancestor of a Tree Node - ALawliet/algorithms GitHub Wiki

https://youtu.be/oib-XsjFa-M

Algo For node 0, 1, ..., n-1, we define a matrix self.dp[][] whose i, jth element indicates the ith node's 2^j parent. Here, i = 0, 1, ..., n-1 and j = 0, 1, ..., int(log2(n)). An important recursive relationship is that

self.dp[i][j] = self.dp[self.dp[i][j-1]][j-1].

In other words, ith node's 2^j parent is ith node's 2^(j-1) parent's 2^(j-1) parent. In this way, lookup is guarenteed to complete in O(logN) time. Note that it takes O(NlogN) to build self.dp.

Time complexity O(NlogN) to pre-processing the tree and O(logN) for each equery thereafter.

class TreeAncestor:

    def __init__(self, n: int, parent: List[int]):
        LOG = int(log2(n)) + 1 #at most 16 for this problem, 2^x = n = LOG
        self.up = [[-1] * LOG for _ in range(n)] #ith node's 2^j parent = up
        # dp[num][pow2]
        for j in range(LOG): # pow2 = LOG
            for v in range(n): # num = v
                if j == 0:
                    self.up[v][0] = parent[v] #2^0 parent
                elif self.up[v][j-1] != -1: # binary jumps
                    self.up[v][j] = self.up[ self.up[v][j-1] ][j-1]
        
        # parent[0] = 0
        # for v in range(n):
            # self.dp[v][0] = parent[v]
            # for j in range(1, m):
                # self.dp[v][j] = self.dp[ self.dp[v][j-1] ][j-1] 
    
    def getKthAncestor(self, node: int, k: int) -> int:
        while k > 0 and node != -1: 
            i = int(log2(k))
            node = self.up[node][i]
            k -= (1 << i) # jth bit is ON in number k
        return node 
    
'''
k = 19 = 10011 = 16 + 2 + 1
int up[N][LOG]
up[v][k] -- 2^j-th ancestor of v (j=5 is 2^5=32nd ancestor of v)

for v = 0 .. N-1:
    up[v][0] = parent[v] # kth ancestor=1, up by 1
    ---
    
    up[v][1] = up[ up[v][0] ][0] # k=2, grandparent (parent of parent)
    up[v][2] = up[ up[v][1] ][1] # k=4, (grandparent of grandparent = 4th ancestor = 2nd ancestor of 2nd ancestor)
    up[v][3] = up[ up[v][2] ][2] # k=8, (up by 4 and up by 4)
  
    for j = 1 .. LOG - 1:
        up[v][j] = up[ up[v][j-1] ][j-1]
        
O(N*log(N))
''' 
// Binary Lifting tutorial https://youtu.be/oib-XsjFa-M
class TreeAncestor {
    vector<vector<int>> up; // int up[N][20];
    vector<int> depth;
    int LOG;
public:
    TreeAncestor(int n, vector<int>& parent) {
        LOG = 0;
        while((1 << LOG) <= n) {
            LOG++;
        }
        up = vector<vector<int>>(n, vector<int>(LOG));
        depth = vector<int>(n);
        // up[v][j] is 2^j -th ancestor of node v
        parent[0] = 0;
        for(int v = 0; v < n; v++) {
            up[v][0] = parent[v];
            if(v != 0) {
                depth[v] = depth[parent[v]] + 1;
            }
            for(int j = 1; j < LOG; j++) {
                up[v][j] = up[ up[v][j-1] ][j-1];
            }
        }
    }
    int getKthAncestor(int node, int k) {
        if(depth[node] < k) {
            return -1;
        }
        for(int j = LOG - 1; j >= 0; j--) {
            if(k >= (1 << j)) {
                node = up[node][j];
                k -= 1 << j;
            }
        }
        return node;
    }
};
⚠️ **GitHub.com Fallback** ⚠️