Leetcode 297:二叉树的序列化与反序列化(超详细的解法!!!)

序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

示例:

你可以将以下二叉树:

    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

如有问题,希望大家指出!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值