思想
二叉树的核心思想是分治和递归,特点是遍历方式。
解题方式常见两类思路:
- 遍历一遍二叉树寻找答案;
- 通过分治分解问题寻求答案;
遍历分为前中后序,本质上是遍历二叉树过程中处理每个节点的三个特殊时间点:
- 前序是在刚刚进入二叉树节点时执行;
- 后序是在将要离开二叉树节点时执行;
- 中序是左子树遍历完进入右子树前执行;
# 前序
1 node
/ \
2 left 3 right
中左右
# 中序
2 node
/ \
1 left 3 right
左中右
# 后序
3 node
/ \
1 left 2 right
左右中
多叉树只有前后序列遍历,因为只有二叉树有唯一一次中间节点的遍历
题目的关键就是找到遍历过程中的位置,插入对应代码逻辑实现场景的目的。
实例
寻找重复的子树 leetcode 652
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
输入:
TreeNode,一棵树的根节点
输出:
List[TreeNode],返回所有重复的子树列表
举例:
输入 root = [1,2,3,4,null,2,4,null,null,4]
返回二叉树子树列表 [[2,4],[4]]
1
/ \
2 3
/ / \
4 2 4
/
4
二叉树的数据存储可以使用链表,也可以使用数组,往往数组更容易表达,根节点从 index=1 处开始存储,浪费 index=0 的位置
left_child = 2 * parent
right_child = 2 * parent + 1
parent = child // 2
分治解
查找重复子树,要在获得子树详细情况的位置进行比对,所以在后续遍历的位置比对是否出现了重复子树。全局一块内存储存出现过的子树。
上例中,全局储存子树 [],重复子树 []
- 根节点 1 出发,在后续遍历的位置进行比对
- 1 遍历到左子树 2,再到左子树 4,左右子树都为空,返回到 2
- 2 的右子树为空,左子树 4,存储到全局子树中 [[4]],返回到根节点 1
- 继续遍历 1 的右子树 3,到 3 的左子树 2,再到 2 的左子树 4 返回
- 2 的左子树 4 存在于全局子树,重复子树增加 [[4]],返回 3
- 继续遍历 3 的右子树 4,左右子树均为空,返回 3
- 3 的左子树 [2,4] 加入全局存储子树,[[4], [2,4]],右子树存在于重复子树不再处理,返回根节点 1
- 1 的左子树 [2,4] 重复,加入重复子树 [[4], [2,4]],右子树 [3,2,4,4] 加入全局子树 [[4], [2,4], [3,2,4,4]]
- 结束遍历,最终返回 [[2,4],[4]]
编码
from typing import Optional, List
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def find_duplicate_subtrees(root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
# 储存全局子树
subtree, duplicate = {}, []
def serialize(root: Optional[TreeNode]) -> str:
nodes = []
def _traverse(node: Optional[TreeNode]):
if node is None:
nodes.append('#')
return
nodes.append(str(node.val))
_traverse(node.left)
_traverse(node.right)
_traverse(root)
return ','.join(nodes)
def traverse(root: Optional[TreeNode]):
# base 条件,叶子空节点直接返回
if root is None:
return
traverse(root.left)
traverse(root.right)
# 后序位置做子树比对
serialize_left = serialize(root.left)
serialize_right = serialize(root.right)
if serialize_left not in subtree:
subtree[serialize_left] = 0
else:
subtree[serialize_left] += 1
if subtree[serialize_left] == 1:
duplicate.append(root.left)
if serialize_right not in subtree:
subtree[serialize_right] = 0
else:
subtree[serialize_right] += 1
if subtree[serialize_right] == 1:
duplicate.append(root.right)
traverse(root)
# 过滤空子树
return [e for e in duplicate if e]
def find_duplicate_subtrees_optimize(root: Optional[TreeNode]) -> List[Optional[TreeNode]]:
# 储存全局子树
subtree, duplicate = {}, []
def traverse(root: Optional[TreeNode]) -> str:
# base 条件,叶子空节点返回占位符
if root is None:
return '#'
left = traverse(root.left)
right = traverse(root.right)
# 后序位置做子树比对
tree = f'{left},{right},{root.val}'
if tree not in subtree:
subtree[tree] = 0
else:
subtree[tree] += 1
if subtree[tree] == 1:
duplicate.append(root)
return tree
traverse(root)
return duplicate
相关
二叉树 0
二叉树 1
二叉树 2
二叉树 3
二叉树 4
二叉树 5
二叉树 6
二叉树 7
二叉树 8
二叉树 9
二叉树 10
二叉树 11
二叉树 12
二叉树 13
二叉树 14
二叉树 15
二叉树 16
二叉树 17
网友评论