今天leetcode上看到一道算法题,了解了一下Binary Lifting,写篇博客详细介绍一下
题目描述:
给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。
请你设计并实现 getKthAncestor(int node, int k) 函数,函数返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 -1 。
树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-ancestor-of-a-tree-node
就这?
class TreeAncestor {
private int n;
private int[] parent;
public TreeAncestor(int n, int[] parent) {
this.n=n;
this.parent = parent;
}
public int getKthAncestor(int node, int k) {
int ancestor = parent[node];
for(int i=1;i<k&&ancestor!=-1;i++){
ancestor = parent[ancestor];
}
return ancestor;
}
}
/**
* Your TreeAncestor object will be instantiated and called as such:
* TreeAncestor obj = new TreeAncestor(n, parent);
* int param_1 = obj.getKthAncestor(node,k);
*/
这不就解决了吗!
结果啪啪打脸
看了一下答案,这道题正解是Binary Lifting,也就是倍增法
Binary Lifting的本质是动态规划,既然是动态规划,那么就要构建一个多维数组并且得到他的推导式。
二维数组,表示第个节点的第个祖先,那么推导式为:
初始条件为
然后依次得到,,, 。。。
如果要查询节点的第个祖先,那么可以分解为
(假设为16位整数)
例如,我们如果要查某个节点m的第11个祖先节点,那么11=0X1011,也就是,我们可以先查节点m的第个祖先为r,再查r的第个祖先为p,再查p的第个祖先为q,那么q就是m的第11个祖先。最多只需要进行log(n)次查找,因此时间复杂度为log(n)。
最后贴出代码
import java.util.Arrays;
public class TreeAncestor {
private int[][] dp;
public TreeAncestor(int n, int[] parent) {
dp = new int[n][16];
for (int[] a : dp) {
Arrays.fill(a, -1);
}
for (int i = 0; i < n; i++) {
dp[i][0] = parent[i];
}
for (int j = 1; j < 16; j++) {
for (int i = 0; i < n; i++) {
if (dp[i][j - 1] != -1) {
dp[i][j] = dp[dp[i][j - 1]][j - 1];
}
}
}
}
public int getKthAncestor(int node, int k) {
for (int i = 0; i < 16; i++) {
if (((1 << i) & k) != 0) {
node = dp[node][i];
}
if (node == -1) {
break;
}
}
return node;
}
}