在上一篇从二叉树开始刷起(一)—— 熟悉递归套路中我们看了几道二叉树前、中、后序遍历的题,在这一节的开始,我觉得有必要先了解一下二叉树的层序遍历应该怎么做。以便引出后面的话题。
首先看一道普通的层序遍历的题:
102. 二叉树的层序遍历
这道题刚读完题目其实是需要注意一点,就是其输出其实是一个二维的数组,所以在安排输出的时候我们也需要注意一下,那我们分析一下这道题应该怎么做吧。
首先我们来看一下还是传统递归的方式,其实也就是DFS的解法,从最开始的根节点开始,对每一层我们都把对应那一层的根节点加入到对应层数组中去,同时对下一层的左右子树执行同样的操作。来一起看一下代码:
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
# DFS解法
res = []
def dfs(index,root):
if not root:return
# 如果某个节点还没有被访问过,就先给她安排一个位置
if len(res) < index:
res.append([])
res[index-1].append(root.val)
if root.left:
# 要突破到下一层
dfs(index+1, root.left)
if root.right:
dfs(index+1, root.right)
dfs(1,root)
return res
咱再来看看迭代的方法怎么做,或者说我们说的BFS的方法应该是什么样的。层序遍历采用BFS的方法会比较好做,只不过本题区别于传统BFS全部往一个队列里边丢的情况,需要单独分开每一层的结果(需要使用一个临时的数组和一个保存结果的数组),对于本题,BFS做的话其关键,还是理解,树这个数据结构是如何构造的,可以理解成一个三个key的json的格式,[val:root_val,left:{val,{}},right:{val…}],这样我们初始化构造队列的时候就是将整棵树全部放进去了,这时候队列里边也只有一个元素;循环体中我们对应的根节点元素取出来之后,整下的事便是将左右子树加入队列,这时队列中便变成了两个元素,依此类推,总之,队列中存放的永远是当前层的所有节点。(这里队列实现方式也可以用python中的双端队列,在其他类型题中可能更加方便)
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
# 先将整个树放到队列里边去,其实就是初始化一个队列,队列(列表)里边只有一个元素,就是那个树
if not root:return []
queue = [root]
# 声明结果list
res = []
while (queue):
# 循环读取队列,看队列里边有几个元素
size = len(queue)
tmp = []
for _ in range(size):
cur_tree = queue.pop(0)
tmp.append(cur_tree.val)
if cur_tree.left:
queue.append(cur_tree.left)
if cur_tree.right:
queue.append(cur_tree.right)
res.append(tmp)
return res
OK,这里的做题套路需要反复思考咀嚼,有必要的话还需要多做几遍,形成肌肉记忆都好。后面咱会用到。
再来看这道题,也是一个大餐。
297. 二叉树的序列化与反序列化
这道题要求实现二叉树的序列化与反序列化,其实就是要求咱把二叉树编码成一个字符串然后再解码回来。
我们先来看看用递归的方法怎么写:
class Codec:
# DFS,递归写法,前序遍历
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
if not root:return "None"
return str(root.val) + "," + self.serialize(root.left) + "," + self.serialize(root.right)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
def dfs(data_list):
root_val = data_list.pop(0)
if root_val == "None":
return
root = TreeNode(root_val)
root.left = dfs(data_list)
root.right = dfs(data_list)
return root
if not data:return []
data_list = data.split(",")
return dfs(data_list)
总体来说还是比较常规的套路,以此我们还可以写出后序遍历的版本,但是中序遍历的版本就写不出来了。
注意后序遍历版本有一个小细节,解码的时候需要先解右子树:
class Codec:
# dfs,后序遍历
def serialize(self, root):
if not root:
return 'None'
return str(self.serialize(root.left)) + "," + str(self.serialize(root.right)) + "," + str(root.val)
def deserialize(self, data):
def dfs(tree_list):
val = tree_list.pop(-1)
if val == "None":
return None
root = TreeNode(val)
root.right = dfs(tree_list)
root.left = dfs(tree_list)
return root
tree_list = data.split(",")
if not tree_list: return None
root = dfs(tree_list)
return root
那我们再看迭代的话应该怎么写:
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
res = ""
if not root: return res
# 基本要素:根节点元素所代表的树全部入队列
queue = [root]
# 判断条件是队列不为空
while queue:
# 每次都只看队首元素,作为当前树
cur_tree = queue.pop(0)
if cur_tree:
# 执行完有效操作就继续将子树入队列
res += str(cur_tree.val) + ","
queue.append(cur_tree.left)
queue.append(cur_tree.right)
else:
res += "None" + ","
return res
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data:return []
data_list = data.split(",")
queue = data_list
root = TreeNode(data_list.pop(0))
queue = [root]
while queue:
# 每次都只看队首元素,作为当前树
cur_tree = queue.pop(0)
if data_list:
left = data_list.pop(0)
if left != "None":
# 执行完有效操作就继续将子树入队列
cur_tree.left = TreeNode(left)
queue.append(cur_tree.left)
else:
cur_tree.left = None
if data_list:
right = data_list.pop(0)
if right != "None":
cur_tree.right = TreeNode(right)
queue.append(cur_tree.right)
else:
cur_tree.right = None
return root
编码相对容易一些,解码的时候需要认真确认往队列中添加元素的时机,以及添加的内容。
以上两种方法的时间复杂度均为O(n),因为每个元素都只被访问了一遍;空间复杂度也均为O(n)。这道题值得时常温习。