本篇全部集中在二叉树相关问题上,是参考东哥的思路进行的练习和思考。东哥有《labuladong 的算法小抄》以及宝藏微信公众号 labuladong,github 也有项目,自来水推荐购买和关注。
我宣布我爱上了递归~ 1.x 这些问题我看了以前的提交都是写的 helper,现在直接在函数签名下面完成函数功能,感觉有点想“装”一下哈哈O(∩_∩)O
看了以前的写法,思路也还是遍历,但是单独写的 helper。针对这个问题,可以直接使用原题函数签名。
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
# 使用递归的形式,通过遍历的思路来合并两棵树
# 本函数需要返回合并后的结果,此结果还需要一定要有指针来接收
if not root1: return root2
if not root2: return root1
root1.val += root2.val
root1.left = self.mergeTrees(root1.left, root2.left)
root1.right = self.mergeTrees(root1.right, root2.right)
return root1
def removeLeafNodes(self, root: TreeNode, target: int) -> TreeNode:
# 借助递归的形式,使用遍历二叉树的思路,来删除指定叶子节点
# 没路了,原地折回
if not root: return None
#递归左右子树,一定要接收返回值
root.left = self.removeLeafNodes(root.left, target)
root.right = self.removeLeafNodes(root.right, target)
# 目标出现,斩掉连接,撤退~
if not root.left and not root.right and root.val == target:
return None
return root
def pruneTree(self, root: TreeNode) -> TreeNode:
# 借助递归的形式,通过遍历二叉树,来完成指定剪枝
# 到头儿了,往回拐
if not root: return None
# 递归左右子树,返回值要有人接
root.left = self.pruneTree(root.left)
root.right = self.pruneTree(root.right)
# 目标出现,切掉,撤退~
if not root.left and not root.right and root.val == 0:
return None
# 返回需要有人接的 root,root 而来,root 而去
return root
前序思路从前往后拿,依次是 root left 和 right;
后序思路从后往前拿,依次是 root right 和 left;
前序后序都可以用递归来做,理论上好像中序也可以类似思路,但是中序思路祖宗 root 藏木于林,我布吉岛它在哪儿,所以无法序列化;
前序:
# 使用前序遍历的思路进行序列化和反序列化
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
# 需要额外定义函数来实现前序遍历序列化,空节点都为 '#'
def my_serialize(root, res):
if not root:
res.append('#')
return
res.append(str(root.val))
my_serialize(root.left, res)
my_serialize(root.right, res)
return res
res = my_serialize(root, [])
if not res: return ''
return ','.join(res)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data: return None
res = collections.deque(data.split(','))
# 需要额外定义函数来实现中序遍历反序列化
# 要有返回值,还一定有人接
def my_deserialize(res):
if not res: return None
cur = res.popleft()
if cur == '#': return None
root = TreeNode(int(cur))
root.left = my_deserialize(res)
root.right = my_deserialize(res)
return root
return my_deserialize(res)
中序:
# 使用中序遍历的思路进行序列化和反序列化
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
# 需要额外定义函数来实现中序遍历序列化,空节点都为 '#'
def my_serialize(root, res):
if not root:
res.append('#')
return
my_serialize(root.left, res)
res.append(str(root.val))
my_serialize(root.right, res)
return res
res = my_serialize(root, [])
if not res: return ''
return ','.join(res)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data: return None
res = collections.deque(data.split(','))
print(res)
# 需要额外定义函数来实现中序遍历反序列化
# 要有返回值,还一定有人接
def my_deserialize(res):
if not res: return None
# 谁能告诉我根节点在哪里?没有人。
# 那算了,溜了溜了,不干了
# cur = res.pop()
# if cur == '#': return None
# root = TreeNode(int(cur))
# # 需要先重建右子树,再重建左子树
# root.right = my_deserialize(res)
# root.left = my_deserialize(res)
# return root
# return my_deserialize(res)
后序:
# 使用后序遍历的思路进行序列化和反序列化
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
# 需要额外定义函数来实现后序遍历序列化,空节点都为 '#'
def my_serialize(root, res):
if not root:
res.append('#')
return
my_serialize(root.left, res)
my_serialize(root.right, res)
res.append(str(root.val))
return res
res = my_serialize(root, [])
if not res: return ''
return ','.join(res)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data: return None
res = collections.deque(data.split(','))
# 需要额外定义函数来实现后序遍历反序列化
# 要有返回值,还一定有人接
def my_deserialize(res):
if not res: return None
cur = res.pop()
if cur == '#': return None
root = TreeNode(int(cur))
root.right = my_deserialize(res)
root.left = my_deserialize(res)
return root
return my_deserialize(res)
关于计算左子树尺寸,你愿意计算右子树尺寸来做也完全可以。这个维护边界的样子和排序算法如出一辙。
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 借助递归的形式,使用遍历二叉树的思路(反过来),构造二叉树
# 因为过程中 preorder 和 inorder 完全没有变
# 只是在不断维护左右子树在 preorder 和 inorder 中的区间,所以需要重新写个函数
def my_build_tree(pre_start, pre_end, in_start, in_end):
# 树为空啊,没有内容
if pre_start > pre_end: return None
# 根节点拿出来
root = TreeNode(preorder[pre_start])
# 借助中序遍历计算左子树的 size
idx = inorder.index(root.val)
left_tree_size = idx - in_start
# 根节点下一个就是左子树的左边界,根据左子树 size 推出来右边界
root.left = my_build_tree(pre_start + 1, pre_start + left_tree_size, in_start, idx - 1)
# 左子树右边界下个位置就是右子树的左边界,它俩挨着坐的嘛
root.right = my_build_tree(pre_start + left_tree_size + 1, pre_end, idx + 1, in_end)
return root
n = len(preorder)
return my_build_tree(0, n - 1, 0, n - 1)
def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 借助递归的形式,使用遍历二叉树的思路(反过来),构造二叉树
# 因为过程中 postorder 和 inorder 完全没有变
# 只是在不断维护左右子树在 postorder 和 inorder 中的区间,所以需要重新写个函数
def my_build_tree(post_start, post_end, in_start, in_end):
# 树为空啊,没有内容
if post_start > post_end: return None
# 根节点拿出来
root = TreeNode(postorder[post_end])
# 借助中序遍历计算左子树的 size
idx = inorder.index(root.val)
left_tree_size = idx - in_start
# 左子树左边界还是在最前面不变,根据左子树 size 推出来右边界
root.left = my_build_tree(post_start, post_start + left_tree_size - 1, in_start, idx - 1)
# 左子树右边界下个位置就是右子树的左边界,它俩挨着坐的嘛,然后右边界不能触碰爸爸 root
root.right = my_build_tree(post_start + left_tree_size, post_end - 1, idx + 1, in_end)
return root
n = len(postorder)
return my_build_tree(0, n - 1, 0, n - 1)
Leetcode 的层序遍历,一个屏幕放不下,幸好我还有个竖屏~
从老到少按辈分都依次装进麻袋;
另外一头开个口子,老祖宗先出来,然后依次在麻袋口守着等自己孩子;
领完的就走了,当上一个辈分正好都领完,麻袋边上排队的正好是下一个辈分。
# 使用层序遍历的思路进行序列化和反序列化
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
# 需要额外定义函数来实现层序遍历序列化,空节点都为 '#'
if not root: return ''
res = [str(root.val)]
que = collections.deque()
que.append(root)
while que:
le = len(que)
temp = []
has_node = False
for i in range(le):
cur = que.popleft()
if not cur.left:
temp.append('#')
else:
has_node = True
temp.append(str(cur.left.val))
que.append(cur.left)
if not cur.right:
temp.append('#')
else:
has_node = True
temp.append(str(cur.right.val))
que.append(cur.right)
# 全为空节点的最后一层不配进家谱
if has_node: res.extend(temp)
return ','.join(res)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data: return None
res = collections.deque(data.split(','))
root = TreeNode(int(res.popleft()))
level_que = collections.deque()
level_que.append(root)
# 从老到少都依次在队里了,现在一个个拿出来
# 之前已出队的非空节点才有孩子可以领
while res:
parent = level_que.popleft()
left = res.popleft()
if left == '#':
parent.left = None
else:
parent.left = TreeNode(int(left))
level_que.append(parent.left)
right = res.popleft()
if right == '#':
parent.right = None
else:
parent.right = TreeNode(int(right))
level_que.append(parent.right)
return root