将前序遍历数组转换为级别顺序遍历数组(反之亦然),前提是二叉树是按 Level Order 填充的
Converting a preorder traversal array to an level order traversal array (or vice versa) given that the binary tree was filled in Level Order
假设您有一棵按级别顺序填充的二叉树,即每个级别都在该级别的任何子节点之前被填充。这样一棵树可以通过它的层序遍历来唯一定义。例如 {1,2,3,4,5,6} 是
1
2 3
4 5 6
先序遍历会生成数组 {1,2,4,5,3,6}
有没有一种方法可以将这些数组中的一个直接转换为另一个,这比生成实际树并对其执行实际遍历更快? (对于有 n 个节点的树)
是的,这两个都可以一次通过。
首先,升级到预购,因为这更容易一些。由于这是一棵水平填充树,对于数组中的任何节点,给定它的索引i
,左子树节点的索引公式为2*i+1
,对于右子树,2*i+2
.因此,我们按预定顺序递归调用它们,将根节点附加到我们想要的数组的后面。
def level_to_pre(arr,ind,new_arr):
if ind>=len(arr): return new_arr #nodes at ind don't exist
new_arr.append(arr[ind]) #append to back of the array
new_arr = level_to_pre(arr,ind*2+1,new_arr) #recursive call to left
new_arr = level_to_pre(arr,ind*2+2,new_arr) #recursive call to right
return new_arr
并这样调用,这里将填充最后一个空白数组。
ans = level_to_pre([1,2,3,4,5,6],0,[])
现在,在我进入关卡前部分之前,请记住 dfs 使用递归,它在幕后使用堆栈。 bfs 使用队列的地方。而我们手头的问题,按级别优先顺序填充数组,基本上是 bfs。因此,我们必须显式维护一个队列来模拟那些递归调用,而不是递归调用(即堆栈)。
我们在这里做的是,给定一个子树的根,我们首先将它添加到答案数组中。现在,与上述问题不同,查找子节点的索引具有挑战性。左边一个很容易,它会在 root 之后立即。为了找到右边的索引,我们计算左子树的总大小。这是可能的,因为我们知道它的水平填充。我们现在使用一个辅助函数 left_tree_size(n)
,它 returns 在给定整棵树的大小的情况下留下子树的大小。因此,除了根的索引之外,在递归的情况下我们将传递的第二个参数是该子树的大小。为了反映这一点,我们在队列中放置了一个 (root,size) 元组。
from math import log2
import queue
def pre_to_level(arr):
que = queue.Queue()
que.put((0,len(arr)))
ans = [] #this will be answer
while not que.empty():
iroot,size = que.get() #index of root and size of subtree
if iroot>=len(arr) or size==0: continue ##nodes at iroot don't exist
else : ans.append(arr[iroot]) #append to back of output array
sz_of_left = left_tree_size(size)
que.put((iroot+1,sz_of_left)) #insert left sub-tree info to que
que.put((iroot+1+sz_of_left,size-sz_of_left-1)) #right sub-tree info
return ans
最后,这里是辅助函数,按照几个例子来弄清楚它为什么起作用。
def left_tree_size(n):
if n<=1: return 0
l = int(log2(n+1)) #l = no of completely filled levels
ans = 2**(l-1)
last_level_nodes = min(n-2**l+1,ans)
return ans + last_level_nodes -1
假设您有一棵按级别顺序填充的二叉树,即每个级别都在该级别的任何子节点之前被填充。这样一棵树可以通过它的层序遍历来唯一定义。例如 {1,2,3,4,5,6} 是
1
2 3
4 5 6
先序遍历会生成数组 {1,2,4,5,3,6}
有没有一种方法可以将这些数组中的一个直接转换为另一个,这比生成实际树并对其执行实际遍历更快? (对于有 n 个节点的树)
是的,这两个都可以一次通过。
首先,升级到预购,因为这更容易一些。由于这是一棵水平填充树,对于数组中的任何节点,给定它的索引i
,左子树节点的索引公式为2*i+1
,对于右子树,2*i+2
.因此,我们按预定顺序递归调用它们,将根节点附加到我们想要的数组的后面。
def level_to_pre(arr,ind,new_arr):
if ind>=len(arr): return new_arr #nodes at ind don't exist
new_arr.append(arr[ind]) #append to back of the array
new_arr = level_to_pre(arr,ind*2+1,new_arr) #recursive call to left
new_arr = level_to_pre(arr,ind*2+2,new_arr) #recursive call to right
return new_arr
并这样调用,这里将填充最后一个空白数组。
ans = level_to_pre([1,2,3,4,5,6],0,[])
现在,在我进入关卡前部分之前,请记住 dfs 使用递归,它在幕后使用堆栈。 bfs 使用队列的地方。而我们手头的问题,按级别优先顺序填充数组,基本上是 bfs。因此,我们必须显式维护一个队列来模拟那些递归调用,而不是递归调用(即堆栈)。
我们在这里做的是,给定一个子树的根,我们首先将它添加到答案数组中。现在,与上述问题不同,查找子节点的索引具有挑战性。左边一个很容易,它会在 root 之后立即。为了找到右边的索引,我们计算左子树的总大小。这是可能的,因为我们知道它的水平填充。我们现在使用一个辅助函数 left_tree_size(n)
,它 returns 在给定整棵树的大小的情况下留下子树的大小。因此,除了根的索引之外,在递归的情况下我们将传递的第二个参数是该子树的大小。为了反映这一点,我们在队列中放置了一个 (root,size) 元组。
from math import log2
import queue
def pre_to_level(arr):
que = queue.Queue()
que.put((0,len(arr)))
ans = [] #this will be answer
while not que.empty():
iroot,size = que.get() #index of root and size of subtree
if iroot>=len(arr) or size==0: continue ##nodes at iroot don't exist
else : ans.append(arr[iroot]) #append to back of output array
sz_of_left = left_tree_size(size)
que.put((iroot+1,sz_of_left)) #insert left sub-tree info to que
que.put((iroot+1+sz_of_left,size-sz_of_left-1)) #right sub-tree info
return ans
最后,这里是辅助函数,按照几个例子来弄清楚它为什么起作用。
def left_tree_size(n):
if n<=1: return 0
l = int(log2(n+1)) #l = no of completely filled levels
ans = 2**(l-1)
last_level_nodes = min(n-2**l+1,ans)
return ans + last_level_nodes -1