本质:DP的思想
1483. 树节点的第 K 个祖先
给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。
树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。
实现 TreeAncestor 类:
- TreeAncestor(int n, int[] parent) 对树和父数组中的节点数初始化对象。
- getKthAncestor(int node, int k) 返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 -1 。
示例 1:
输入: [“TreeAncestor”,”getKthAncestor”,”getKthAncestor”,”getKthAncestor”] [[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]] 输出: [null,1,0,-1] 解释: TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]); treeAncestor.getKthAncestor(3, 1); // 返回 1 ,它是 3 的父节点 treeAncestor.getKthAncestor(5, 2); // 返回 0 ,它是 5 的祖父节点 treeAncestor.getKthAncestor(6, 3); // 返回 -1 因为不存在满足要求的祖先节点
提示:
- 1 <= k <= n <= 5 * 104
- parent[0] == -1 表示编号为 0 的节点是根节点。
- 对于所有的 0 < i < n ,0 <= parent[i] < n 总成立
- 0 <= node < n
- 至多查询 5 * 104 次
思路:
倍增思想
直接遍历寻找每个节点的祖先会超时
已知:当前节点父节点的父节点是当前节点的二代祖先
故可以使用倍增的思想,用父节点的一代祖先更新当前节点的二代祖先
以此类推,用当前节点的二代祖先的二代祖先更新当前节点的四代祖先
。。。
本质是一种DP
查询时:利用log
的乘法变加法运算,快速找到当前节点的k
代祖先
class TreeAncestor {
int N = 20;
int[][] root;
int n;
public TreeAncestor(int n, int[] parent) {
this.n = n;
root = new int[n][N];
for (int i = 0; i < n; i++)
Arrays.fill(root[i], -1);
for (int i = 0; i < n; i++) {
root[i][0] = parent[i];
}
for (int j = 1; j < N; j++) {
for (int i = 0; i < n; i++) {
int fa = root[i][j - 1];
if (fa != -1) {
root[i][j] = root[fa][j - 1];
}
}
}
}
public int getKthAncestor(int node, int k) {
for (int i = 0; i < N; i++) {
if ((k >> i & 1) == 1) {
node = root[node][i];
if (node == -1)
break;
}
}
return node;
}
}
/**
* Your TreeAncestor object will be instantiated and called as such:
* TreeAncestor obj = new TreeAncestor(n, parent);
* int param_1 = obj.getKthAncestor(node,k);
*/
异曲同工:
1483的解题步骤中有这样一步,之前算过的内容在之后会被用到
类似的问题有AcWing 1295. X的因子链,在分解质因数时,如果提前预处理了线性筛,可以在logn
的时间复杂度内求出x
的算术基本定理的结果。