参考:https://labuladong.online/algo/data-structure/serialize-and-deserialize-binary-tree/
1. 前中后序和二叉树的唯一性
- 给定空指针的前提下,只有前序和后序可以唯一确定一颗二叉树;中序不可以。原因是中序遍历无法确定二叉树根节点的位置。
- 不给定空指针的前提下,只靠一种遍历结果是无法还原二叉树的;
给定前序和中序,或者中序和后序可以还原;只给前序和后序是无法唯一还原的。
2. leetcode 297 二叉树的序列化和反序列化
https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/
2.1 前序遍历解法
先把序列化结果存在数组res中,再通过逗号拼接,这样写起来比较简洁。
- 序列化:需要外部变量和辅助函数。将根节点加入数组,剩下的交给递归。
这里注意,也可以不用外部变量,用分解的方式来做。每次递归的时候是res.extend(self.serializeHelper(root.left)). 但是尽量避免这种数组操作,避免内存开销过大。 - 反序列化:前序是从左向右,所以先构造根节点,然后递归地构造左右子树。和序列化的写法是对称的。
注意需要判断节点值是否为‘#’
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
self.res = [] # 新建一个外部变量,存放序列化的结果
self.serializeHelper(root)
return ','.join(self.res)
def serializeHelper(self, root):
if not root:
self.res.append('#')
return
self.res.append(str(root.val)) # 每次把根节点append到res里,其余的交给递归
self.serializeHelper(root.left)
self.serializeHelper(root.right)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
return self.deserializeHelper(data.split(','))
def deserializeHelper(self, arr):
if not arr:
return
rootValue = arr.pop(0) # 和序列化类似,每次把最左边的根节点出队加入root,其余的交给递归
if rootValue == '#':
return
root = TreeNode(int(rootValue))
root.left = self.deserializeHelper(arr)
root.right = self.deserializeHelper(arr)
return root
2.2 后序遍历解法
序列化部分和前序类似;
反序列化部分,先构造根节点,然后递归地构造左右子树。注意因为后序是从右到左,所以要先构造右子树,再构造左子树
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
self.res = []
self.serializeHelper(root)
return ','.join(self.res)
def serializeHelper(self, root):
if not root:
self.res.append('#')
return
self.serializeHelper(root.left)
self.serializeHelper(root.right)
self.res.append(str(root.val))
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
return self.deserializeHelper(data.split(','))
def deserializeHelper(self, arr):
if not arr:
return
rootValue = int(arr.pop(-1))
if rootValue == '#':
return
root = TreeNode(rootValue)
root.right = self.deserializeHelper(arr)
root.left = self.deserializeHelper(arr)
return root
2.3 层序遍历解法
- 序列化比较简单,根据层序遍历做一些改动即可;
- 反序列化,使用指针i代表node的子节点。每构建一个子节点,i向后移一位。
- 循环条件可以写成while q,也可以写成 while i < len(nodes), 两者是等价的
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
if not root:
return '#'
q = deque()
q.append(root)
res = []
while q:
sz = len(q)
for i in range(sz):
cur = q.popleft()
if not cur:
res.append('#')
else:
res.append(str(cur.val))
q.append(cur.left)
q.append(cur.right)
# print(res)
return ','.join(res)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data:
return
nodes = data.split(',')
if nodes[0] == '#':
return
root = TreeNode(int(nodes[0]))
q = deque([root])
i = 1
while q: # 等价于while i < len(nodes)
node = q.popleft()
if (nodes[i] != '#'):
node.left = TreeNode(int(nodes[i]))
q.append(node.left)
i += 1
if (i < len(nodes) and nodes[i] != '#'):
node.right = TreeNode(int(nodes[i]))
q.append(node.right)
i += 1
return root
3. 序列化的应用:寻找重复的子树
https://leetcode.cn/problems/find-duplicate-subtrees/description/
思路:对于某个节点,需要知道两件事:
- 以本节点为根节点的子树长什么样子。即本节点+左右子树的样子,可以用二叉树的序列化来描述。
- 其他子树长什么样子:遍历所有的节点,对每个节点都用序列化的形式记录下来,存放在一个HashMap里。map的key是序列化后的字符串,value是出现的次数。
- res 赋值的条件:freq 严格等于1(不是大于1否则res中的子树会重复)
class Solution(object):
def __init__(self):
self.subTrees = {}
self.res = []
def findDuplicateSubtrees(self, root):
"""
:type root: TreeNode
:rtype: List[TreeNode]
"""
self.serialize(root)
return self.res
def serialize(self, root):
if not root:
return '#'
left = self.serialize(root.left)
right = self.serialize(root.right)
rootStr = left + ',' + right + ',' + str(root.val)
freq = self.subTrees.get(rootStr, 0)
if freq == 1: # 注意,freq = 1时说明已经有一个相同结构的子树了,此时res中添加root。
self.res.append(root)
self.subTrees[rootStr] = freq + 1
return rootStr