题目
二叉树之字形标记路径
小U在研究一棵无限的二叉树,这棵树具有特殊的标记方式。每个节点都有两个子节点,并且树中的节点逐行依次按“之”字形进行标记:
- 在奇数行(例如,第1行、第3行、第5行……),节点按照从左到右的顺序进行标记。
- 在偶数行(例如,第2行、第4行、第6行……),节点按照从右到左的顺序进行标记。
1
/ \
3 2
/ \ / \
4 5 6 7
给定树上某个节点的标号 label,你的任务是返回从根节点到标号为 label 的节点的路径。路径中的节点依次为经过的节点标号。
测试样例
样例1:
输入:
label = 14
输出:[1, 3, 4, 14]
样例2:
输入:
label = 26
输出:[1, 2, 6, 10, 26]
样例3:
输入:
label = 1
输出:[1]
题目分析
题目要求在一个无限的二叉树中,找到从根节点到给定节点 label 的路径。这棵二叉树的节点按照“之”字形进行标记,即奇数层从左到右标记,偶数层从右到左标记。我们需要找到从根节点到指定节点的路径,并返回路径上的所有节点标号。
- 关键点
- 确定节点所在的层数:通过
log2(label)计算节点所在的层数level。 - 逆向寻找路径:从给定的
label开始,不断向上回溯到根节点。需要注意的是,由于“之”字形的标记方式,需要根据节点所在层是否为偶数来调整计算父节点的方法。 - 记录路径:每找到一个父节点,将其添加到路径列表中,直到达到根节点。
- 反转路径:因为是从下往上找的,所以最后需要将路径反转,使其从根节点开始
解题步骤
-
初始化路径列表:创建一个空列表
result用于存储路径。 -
循环回溯父节点:
- 使用
while循环,条件是label不等于1。 - 计算当前节点所在的层数
level。 - 根据层数的奇偶性,计算当前节点在标准情况下的值
standard_label。 - 计算父节点的标准值
parent_label。 - 根据父节点所在的层数,决定是否需要转换回“之”字形的值。
- 更新
label为父节点的值,并将其添加到路径列表中。
- 使用
-
添加根节点:将根节点
1添加到路径列表中。 -
反转路径:将路径列表反转,使其从根节点开始。
代码实现
import math
def solution(label):
result = []
while label != 1:
result.append(label)
# 当前节点所在层数
level = int(math.log2(label))
# 计算 label 在标准情况下的值
if level % 2 == 0: # 偶数层
standard_label = label
else: # 奇数层
standard_label = (2**level + 2**(level + 1) - 1 - label)
# 计算父节点的值
parent_label = (standard_label // 2)
# 如果父节点在偶数层,需要转换回“之”字形的值
if (level - 1) % 2 == 0: # 父节点在偶数层
label = parent_label
else: # 父节点在奇数层
label = (2**(level - 1) + 2**level - 1 - parent_label)
result.append(1) # 添加根节点
return result[::-1] # 反转列表,使路径从根节点开始
复杂度分析
时间复杂度
- 计算层数:每次计算当前节点所在的层数
level需要使用math.log2(label),这是一个常数时间操作。 - 回溯父节点:从给定的
label节点回溯到根节点1。每次回溯一步,label的值会减少大约一半。因此,回溯的次数与label的二进制位数成正比,即 。
空间复杂度
- 路径列表:用列表来存储从根节点到给定节点
label的路径。最坏情况下,路径长度为 ,因为这是从根节点到任意节点的最大路径长度。 - 其他变量:除了路径列表外,还要用一些额外的变量存储当前节点的层数、标准位置等信息。这些变量占用的空间是常数级别的。
总结
主要是根据label找到该节点对应的层,以及根据根据某个节点找到它的父节点的label,一直往上找,直到根节点,最后反转。