LeetCode 1104. 二叉树寻路
题目描述


题意分析
一棵无限满二叉树按层连续编号,但相邻层的编号方向相反:根所在第一行从左向右,第二行从右向左,之后交替。给定一个节点标签
label,返回从根到它经过的全部标签,包含根和目标本身。每层使用的数值范围与普通层序编号相同,只是左右方向可能反转。因此题目不是在普通二叉树中按标签直接除二,也不需要构造或遍历整棵无限树;每个节点的父亲唯一,可以从目标反向求路径。
解法:从节点回溯到根
核心思路
[!blue]
代码把根记为第零层。第
level层的标签范围是start = 2^level到end = 2^(level + 1) - 1,不论该层朝哪边编号,这两个端点都不变。同层某个标签反转方向后的对应值为start + end - label,因为左右对称位置的标签和固定。普通从左到右编号时,孩子编号除以二并向下取整,就是父编号。若当前层是倒序、父层是正序,先把当前标签镜像回普通方向,再除二,得到父标签
floor((start + end - label) / 2)。若当前层是正序、父层是倒序,先求普通父编号
floor(label / 2),再在父层的范围[start / 2, start - 1]内镜像。父标签为start / 2 + start - 1 - floor(label / 2),化简后仍等于上面的统一公式。因此无需每层单独维护方向标记,只要更新本层端点就能求父标签。先根据标签大小找到当前层,从目标开始循环:记录当前标签,套用公式转到父节点,层号减一。每轮严格向上一层,最终记录根标签一;根再转移为零后结束,不会真的继续处理负层号。
这样得到的是目标到根的逆向路径,最后反转列表即可。所有计算只涉及路径上的节点,开销由树高而不是标签范围中的全部节点决定。
解题步骤
- 从零层开始递增层号,找到包含
label的二进制层级范围。- 将当前标签加入路径,计算本层
start、end。- 令
label = (start + end - label) / 2,层号减一,继续向上。- 标签变为零后结束,反转已记录路径并返回。
代码实现
class Solution {
public List<Integer> pathInZigZagTree(int label) {
int level = 0;
while ((1 << (level + 1)) <= label) {
level++;
}
List<Integer> path = new ArrayList<>();
while (label > 0) {
path.add(label);
// 用当前层的最小、最大标签确定镜像关系。
int start = 1 << level;
int end = (start << 1) - 1;
// 相邻层方向相反,利用本层端点统一求父节点标签。
label = (start + end - label) / 2;
level--;
}
// 当前记录从目标到根,反转后才是题目要求的方向。
Collections.reverse(path);
return path;
}
}
func pathInZigZagTree(label int) []int {
level := 0
for (1 << (level + 1)) <= label {
level++
}
path := make([]int, 0, level+1)
for label > 0 {
path = append(path, label)
// 用当前层的最小、最大标签确定镜像关系。
start := 1 << level
end := (start << 1) - 1
// 相邻层方向相反,利用本层端点统一求父节点标签。
label = (start + end - label) / 2
level--
}
// 当前记录从目标到根,反转后才是题目要求的方向。
for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
path[i], path[j] = path[j], path[i]
}
return path
}
复杂度分析
- 时间复杂度:$O(\log(label + 1))$,定位层数、向上回溯和反转都只处理与层数同阶的工作,
label指原始目标标签。- 空间复杂度:返回路径占 $O(\log(label + 1))$,除此之外只保存当前标签、层号与端点,辅助空间为 $O(1)$。
关键点总结
[!green]
- 方向交替不改变每层数值范围,层内镜像由端点和减去当前标签得到。
- 当前层倒序与父层倒序两种情况,都能归结为同一个父标签公式。
- 从目标求唯一父节点更直接,反转后才得到根到目标的要求顺序。
易错点总结
[!yellow]
- 直接用标签除二,忽略了之字形标签与普通位置编号的区别。
- 只在当前层倒序时修正,遗漏当前层正序但父层倒序的情况。
- 将代码的零基层号与题目的一基行号混用,会得到错误的幂次范围。
- 在标签为一之前就停止,导致路径中缺少根节点。
- 忘记反转,返回的是目标到根,而非根到目标。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 662. 二叉树最大宽度 | 中等 | 普通位置编号满足父子倍数关系,本题锯齿标签需先做层内镜像,不能直接除2。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!