序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 "[1,2,3,null,null,4,5]"
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
解题思路
我们首先想到的做法就是通过树的前序、中序或者后续遍历获取字符串,然后在通过获得的字符串再反转回树。
这个问题实际和Leetcode 105:从前序与中序遍历序列构造二叉树(最详细的解法!!!)、Leetcode 106:从中序与后序遍历序列构造二叉树(最详细的解法!!!)是相同问题,显然我们可以通过获取两种遍历的字符串。但是这样做太麻烦了,我们有更好的做法。
在存储字符串的时候,我们可以同时存储一些信息,通过这些信息,我们只需要一种字符串就可以得到我们的结果。
首先考虑前序遍历的解法,我们可以对空节点存储为'#'
,那么例子中的树就可以存储为1 2 # # 3 4 # # 5 # #
。那么这个结果可以反序列化吗?显然是可以的。
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
res = ""
def preOrder(root):
nonlocal res
if not root:
res += '# '
return
res += str(root.val) + ' '
preOrder(root.left)
preOrder(root.right)
preOrder(root)
return res
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
datas = data.split()
def deOrder():
val = datas.pop(0)
if val == '#':
return
root = TreeNode(int(val))
root.left = deOrder()
root.right = deOrder()
return root
return deOrder()
接着考虑后序遍历的解法,我们可以对空节点存储为'#'
,那么例子中的树就可以存储为# # 2 # # 4 # # 5 3 1
。那么这个结果可以反序列化吗?显然也是可以的,而且恰好和前序遍历是对称关系。
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
res = ""
def postOrder(root):
nonlocal res
if not root:
res += '# '
return
postOrder(root.left)
postOrder(root.right)
res += str(root.val) + ' '
postOrder(root)
return res
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
datas = data.split()
def deOrder():
val = datas.pop()
if val == '#':
return
root = TreeNode(int(val))
root.right = deOrder()
root.left = deOrder()
return root
return deOrder()
那么这种做法对中序遍历依旧有效吗?如果采用这种思路的话,中序遍历的结果就是# 2 # 1 # 4 # 3 # 5 #
。可以反序列化吗?不行,可以得到多个结果。例如
1
/ \
2 4
\
3
\
5
为什么呢?因为我们无法通过上面这种方式确定根节点位置。那为什么前序遍历和后序遍历可行呢?因为可以通过n # #
确定n
为根(前序遍历),可以通过# # n
确定n
为根(后序遍历),而且是唯一的,但是中序遍历不唯一。
那么要怎么办?实际上上面已经指明了关键问题所在,就是确定根节点位置。其中一种解法就是将右子树通过括号括起来,例如题目中的例子可以序列化为2 1 [ 4 3 [ 5 ] ]
。
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
def inOrder(root):
res = ''
if not root:
return ''
l, r = inOrder(root.left), inOrder(root.right)
if l:
res += l + ' '
res += str(root.val)
if r:
res += ' [ ' + r + ' ]'
return res
return inOrder(root)
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
datas, i = data.split(), 0
def deOrder():
nonlocal i
root = None
while i < len(datas):
cur = datas[i]
i += 1
if cur == '[':
root.right = deOrder()
elif cur == ']':
return root
else:
t = TreeNode(int(cur))
t.left = root
root = t
return root
return deOrder()
当然最简单的实现方式就是通过层序遍历(≧▽≦)/啦啦啦!!!层序遍历的话,通过空节点填充'#'
即可实现。
class Codec:
def serialize(self, root):
"""Encodes a tree to a single string.
:type root: TreeNode
:rtype: str
"""
if not root:
return ""
q, res = [root], ""
while q:
pre = q.pop(0)
if not pre:
res += '# '
else:
q.append(pre.left)
q.append(pre.right)
res += str(pre.val) + ' '
return res
def deserialize(self, data):
"""Decodes your encoded data to tree.
:type data: str
:rtype: TreeNode
"""
if not data:
return None
datas = data.split()
root = TreeNode(int(datas.pop(0)))
q = [root]
while q:
pre = q.pop(0)
l, r = datas.pop(0), datas.pop(0)
if l != '#':
pre.left = TreeNode(int(l))
q.append(pre.left)
if r != '#':
pre.right = TreeNode(int(r))
q.append(pre.right)
return root
reference:
https://leetcode.com/problems/serialize-and-deserialize-binary-tree/discuss/160613/C%2B%2B-Inorder-recursive-solution
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!