这道题简单理解就是从上到下依次输出二叉树中每层最右边的结点。
经实际检验发现,BFS(宽搜)变体,DFS(深搜)变体都能解决该问题。以下会用这两种方式解决本题,顺带回顾BFS以及DFS模板。
以下讨论均基于本题的示例图:
1. 宽搜与深搜
1.1 宽搜
宽搜的核心是用队列实现,每次从队头取出一个结点,同时从队尾输入该节点的左右儿子结点。
import queue
def bfs(root):
q = queue.Queue()
q.put(root)
ans = []
'''
这个地方注意一个小问题,就是不能直接写while q,否则会陷入死循环。
对于用q = queue.Queue()定义的队列来说,想取出队列的长度只能用q.qsize(),
而不能使用len_q = len(q)
'''
while q.qsize():
h = q.get()
ans.append(h.val)
if h.left:
q.put(h.left)
if h.right:
q.put(h.right)
return ans
ans = bfs(root)
print(ans)
输出结果:
1.2 深搜
深搜用递归实现,深搜模板是先递归左子树,再递归右子树,但是本题所使用的深搜变体是先遍历右子树,再遍历左子树,第二节会讲到。
ans = []
def dfs(root):
'''
深搜里面有一个小的注意点,就是说ans = []不能放在dfs()函数内部定义,
因为dfs是递归执行的,如果ans = []放在函数内部那就会每次递归时重新更新ans为[],
之前ans中积累下来的值就没用了。
'''
global ans
if root == None:
return []
ans.append(root.val)
if root.left: dfs(root.left)
if root.right: dfs(root.right)
return ans
ans = dfs(root)
ans
2. 改进后的宽搜与深搜
传统的模板并不能解决本题,需要对其进行变换。
2.1 宽搜变体
宽搜模板只是按照层序依次输出,但是本题需要区分不同层,依次关键问题在于怎样对模板进行改进,使其能够区分不同层的结点。
方法是,在往队列中加入下一层结点之前,先统计当前队列的长度。可以理解成动物的代际更替。在繁殖下一代之前,先统计当前代中动物的个数,当前代中的最后一个就是我们要找的当前层中的最后一个结点。
import queue
def bfs2(root):
if root == None: return []
q = queue.Queue()
q.put(root)
ans = []
while q.qsize():
len_q = q.qsize()
# for循环这一步的本质就是在遍历当前层
for i in range(len_q):
h = q.get()
if i == len_q - 1: ans.append(h.val)
if h.left: q.put(h.left)
if h.right: q.put(h.right)
return ans
ans = bfs2(root)
print(ans)
2.2 深搜变体
深搜变体最巧妙的地方就是先遍历右子树,再去遍历左子树,并通过ans数组长度与当前层数的比较判断是否加入当前结点。
ans = []
def dfs2(root, depth):
global ans
if root == None: return []
if len(ans) == depth: ans.append(root.val)
if root.right: dfs2(root.right, depth + 1)
if root.left: dfs2(root.left, depth + 1)
return ans
ans = dfs2(root, 0)
print(ans)