1483. 树节点的第 K 个祖先
最开始自己的思路竟然是想构建多叉树,这样实在太蠢了,还是直接学习优秀的官解吧。
学习历程
暴力解法学习
- 举例说明:
n = 7 parent = [-1, 0, 0, 1, 1, 2, 2]
- 一共七个节点 节点的值分别是:
0,1,2,3,4,5,6
-1
:表示根节点ex:寻找节点6的祖先节点
- 第一个祖先节点:
parent[6] = 2
- 第二个祖先节点:
parent[2] = 0
- 第三个祖先节点:
parent[0] = -1
-1 不存在,所以不存在第三个祖先节点
- 此解法时间复杂度
O(k)
,果不其然超时了
class TreeAncestor {
private int[] parent;
public TreeAncestor(int n, int[] parent) {
this.parent = parent;
}
public int getKthAncestor(int node, int k) {
int cnt = 0;
if(cnt == k) {
return cnt;
}
while (node != -1) {
node = parent[node];
cnt++;
if(cnt == k) {
return node;
}
}
return -1;
}
}
优化暴力算法
- 预处理节点?需要预处理出哪些节点?----算了,想不起来,直接往下看
- 预处理出每一个节点的第个祖先节点,即第 2、4、6、8......个祖先节点
- ex:一个节点 node 的第一个祖先节点是
parent[node]
- ???还是看不懂,继续往下
- 任意 k 可以分解为若干不同的 2 的幂,ex:
13 = 8 + 4 + 1
- 所以只需要预处理出这些 祖先节点,就可以快速地到达任意第 k 个祖先节点。
- 看到这里明白了一些,但是如何处理?继续往下看
- ex:
- 所以,可以先往上跳 8步、4步 最后 1 步。也可以 4->8->1 或者 1->8->4
- 无论如何只需要三次就能找到目标第13个祖先节点
如何实现该算法
- 在构造函数
TreeAncestor
中,预处理出每一个节点x
的第个祖先节点,记作
- 若第个祖先节点不存在,则
- 计算方式如下
- 先枚举
i
,再枚举x
。ex:cache[x][0]
表示节点x
的第个祖先节点,所以有
cache[x][0] = parent[x]
cache[x][1]
:表示节点x
的第个祖先节点->cache[x][1] = cache[cache[x][0]][0]
- 解释:求节点
x
的第 2 个祖先节点 - 其第一个祖先节点是
parent[x]
,相当于求parent[x]
的祖先节点,即parent[parent[x]]
- 等同于求
cache[parent[parent[x]]][0]
- 又因为
parent[x] = cache[x][0]
- 所以有
cache[x][1] = cache[cache[x][0][0]
- 我丢--真的太妙了
- 那么可以推导出
cache[x][i+1] = cache[cache[x][i]][i]
- 如果
cache[x][i] = -1
则cache[x][i+1] = -1
- 对于
i+1
而言最多为
- ex:当,
- 意味着最多需要预处理个祖先节点
- 构造函数预处理
- 首先要考虑对于参数
n
,最多需要预处理多少个节点 - 即如何求
i
private int[][] cache;
public TreeAncestor(int n, int[] parent) {
// numberOfLeadingZeros 计算 数字 n 对应的二进制数的长度
int m = 32 - Integer.numberOfLeadingZeros(n);
// 这里对于数字 1->n 分别预处理其 2^(0->m) 个节点的数据
cache = new int[n][m];
for(int i = 0;i < n;i++) {
cache[i][0] = parent[i];
}
for(int i = 0;i < m - 1;i++) {
for(int x = 0;x < n;x++) {
int p = cache[x][i];
cache[x][i + 1] = p < 0?-1:cache[p][i];
}
}
}
- 寻找第 k 个祖先节点
- getKthAncestor
public int getKthAncestor(int node, int k) {
int m = 32 - Integer.numberOfLeadingZeros(k);
for(int i = 0;i < m;i++) {
if(((k >> i) & 1) == 1) {
node = cache[node][i];
if(node < 0) {
break;
}
}
}
return node;
}
b. 另一种写法
public int getKthAncestor(int node, int k) {
// 这里不断去去掉 k 最低位的 1
// 相当于 1 4 8 这样跳
for(;k > 0 && node != -1;k &= k - 1) {
node = cache[node][Integer.numberOfTrailingOfZeros(k)];
}
return node;
}
- 真是太妙了
参考