文章目录

  • 1. 题目
    • 1.1 示例
    • 1.2 说明
    • 1.3 提示
    • 1.4 进阶
  • 2. 解法一(前序遍历)
    • 2.1 分析
    • 2.2 解答
    • 2.3 复杂度
  • 3. 解法二(后序遍历)
    • 3.1 分析
    • 3.2 解答
    • 3.3 复杂度
  • 4. 解法三(广度优先遍历)
    • 4.1 分析
    • 4.2 解答
    • 4.3 复杂度

1. 题目

序列化是将一个数据结构或者对象转换为连续的比特位的过程,借此可以将转换后的结果存储在一个文件或者内存缓冲区中,同时也可以通过网络传输到另一台计算机中,采取某种方式重构得到原数据。

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

1.1 示例

  • 示例 1 1 1:

    • 输入: root = [1, 2, 3, null, null, 4, 5]
    • 输出: [1, 2, 3, null, null, 4, 5]

  • 示例 2 2 2:

    • 输入: root = []
    • 输出: []
  • 示例 3 3 3:

    • 输入: root = [1]
    • 输出: [1]
  • 示例 4 4 4:

    • 输入: root = [1, 2]
    • 输出: [1, 2]

1.2 说明

  • 来源:力扣(LeetCode)
  • 链接:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree

1.3 提示

  • 树中结点数在范围 [ 0 , 1 0 4 ] [0,\textit{ }10^4] [0, 104] 内
  • − 1000 ≤ N o d e . v a l ≤ 1000 -1000 \le Node.val \le 1000 −1000≤Node.val≤1000

1.4 进阶

你可以使用三种以上的方法解决该问题么?

2. 解法一(前序遍历)

2.1 分析

此题分为两个部分,即二叉树的序列化和反序列化,而序列化过程实际上是考察二叉树的几种遍历算法,典型的二叉树遍历算法分为两大类,即深度优先遍历和广度优先遍历,而深度优先遍历又可以继续分为前序遍历中序遍历后序遍历,关于二叉树的这些遍历算法的详细介绍,可以参考本博主先前的一篇博文:【数据结构Python描述】树的前序、中序、后序、广度优先遍历详解及Python手工实现。

需要特别注意的是,上述前序、后序、广度优先遍历算法,对于一般结构的树也均适用,而中序遍历算法仅适用于二叉树;然而对于此题,使用中序遍历算法虽然可以实现二叉树的序列化,但是无法实现二叉树的反序列化,因为使用中序遍历后无法确切知道序列化后的根结点的位置,对此看完本篇题解可能才有比较清晰的理解。

下面先通过前序遍历的方式实现二叉树的序列化以及对应的反序列化,序列化的代码参考博主上述的博文比较容易理解,困难的是如何理解反序列化的代码实现,而实现方式的递归本质又提高了理解的门槛。

实际上,我们不需要去想递归调用的每一层究竟是如何执行的,只要记住反序列化的代码和序列化的代码具有对称性的,例如:在序列化方法 _preorder_serialize 中会先执行对当前结点遍历操作,然后对该结点执行左右子结点的递归调用;在反序列化方法 _preorder_deserialize 中,先对当前结点执行左右子结点的递归构造,然后再返回当前结点。

2.2 解答

from collections import deque
from json import dumps, loads
from typing import List, Deque, Optionalclass TreeNode(object):def __init__(self, val: int):self.val = valself.left = Noneself.right = Noneclass SerializerDeserializer:def _preorder_serialize(self, root: TreeNode, tree: List[Optional[int]]):if not isinstance(root, TreeNode):tree.append(None)returntree.append(root.val)self._preorder_serialize(root.left, tree)self._preorder_serialize(root.right, tree)def preorder_serialize(self, root: TreeNode) -> str:tree = []self._preorder_serialize(root, tree)return dumps(tree)def _preorder_deserialize(self, tree: Deque[Optional[int]]) -> Optional[TreeNode]:if len(tree) == 0:returnleftmost = tree.popleft()if leftmost is None:returnroot = TreeNode(leftmost)root.left = self._preorder_deserialize(tree)root.right = self._preorder_deserialize(tree)return rootdef preorder_deserialize(self, data: str) -> TreeNode:tree = deque(loads(data))return self._preorder_deserialize(tree)def preorder_serialize_deserialize(serializer: SerializerDeserializer, root: TreeNode):serialized_tree = serializer.preorder_serialize(root)print(serialized_tree)  # [1, 2, null, null, 3, 4, null, null, 5, null, null]deserialized_root = serializer.preorder_deserialize(serialized_tree)reconstructed_tree = []def preorder_traverse(tree_root: TreeNode):if tree_root is None:reconstructed_tree.append(None)returnreconstructed_tree.append(tree_root.val)preorder_traverse(tree_root.left)preorder_traverse(tree_root.right)preorder_traverse(deserialized_root)print(reconstructed_tree)  # [1, 2, None, None, 3, 4, None, None, 5, None, None]print(dumps(reconstructed_tree))  # [1, 2, null, null, 3, 4, null, null, 5, null, null]def main():node7 = TreeNode(5)node6 = TreeNode(4)node3 = TreeNode(3)node3.left = node6node3.right = node7node2 = TreeNode(2)node1 = TreeNode(1)node1.left = node2node1.right = node3root = node1serializer = SerializerDeserializer()preorder_serialize_deserialize(serializer, root)if __name__ == '__main__':main()
  • 执行用时: 100 ms , 在所有 Python3 提交中击败了 88.53% 的用户;
  • 内存消耗: 19.9 MB , 在所有 Python3 提交中击败了 11.32% 的用户。

2.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n) ;
  • 空间复杂度: O ( n ) O(n) O(n) 。

3. 解法二(后序遍历)

3.1 分析

在理解了上述基于前序遍历算法的序列化和反序列化之后,使用后序遍历算法实现相同需求就简单很多,然而需要特别注意两点:

  • 与前序遍历不同的是,通过后序遍历序列化后得到结果后,在反序列化时,根结点位于序列化结果的最后;
  • 在使用后序遍历的序列化结果反序列化时,由于反序列顺序是从右往左,而此时先被反序列化的是右子结点,然后才是左子结点。

3.2 解答

from collections import deque
from json import dumps, loads
from typing import List, Deque, Optionalclass TreeNode(object):def __init__(self, val: int):self.val = valself.left = Noneself.right = Noneclass SerializerDeserializer:def _postorder_serialize(self, root: TreeNode, tree: List[Optional[int]]):if not isinstance(root, TreeNode):tree.append(None)returnself._postorder_serialize(root.left, tree)self._postorder_serialize(root.right, tree)tree.append(root.val)def postorder_serialize(self, root: TreeNode) -> str:tree = []self._postorder_serialize(root, tree)return dumps(tree)def _postorder_deserialize(self, tree: Deque[Optional[int]]) -> Optional[TreeNode]:if len(tree) == 0:returnrightmost = tree.pop()if rightmost is None:returnroot = TreeNode(rightmost)root.right = self._postorder_deserialize(tree)root.left = self._postorder_deserialize(tree)return rootdef postorder_deserialize(self, data: str) -> TreeNode:tree = deque(loads(data))return self._postorder_deserialize(tree)def postorder_serialize_deserialize(serializer: SerializerDeserializer, root: TreeNode):serialized_tree = serializer.postorder_serialize(root)print(serialized_tree)  # [null, null, 2, null, null, 4, null, null, 5, 3, 1]deserialized_root = serializer.postorder_deserialize(serialized_tree)reconstructed_tree = []def postorder_traverse(tree_root: TreeNode):if tree_root is None:reconstructed_tree.append(None)returnpostorder_traverse(tree_root.left)postorder_traverse(tree_root.right)reconstructed_tree.append(tree_root.val)postorder_traverse(deserialized_root)print(reconstructed_tree)  # [None, None, 2, None, None, 4, None, None, 5, 3, 1]print(dumps(reconstructed_tree))  # [null, null, 2, null, null, 4, null, null, 5, 3, 1]def main():node7 = TreeNode(5)node6 = TreeNode(4)node3 = TreeNode(3)node3.left = node6node3.right = node7node2 = TreeNode(2)node1 = TreeNode(1)node1.left = node2node1.right = node3root = node1serializer = SerializerDeserializer()postorder_serialize_deserialize(serializer, root)if __name__ == '__main__':main()
  • 执行用时: 120 ms , 在所有 Python3 提交中击败了 63.03% 的用户;
  • 内存消耗: 19.4 MB , 在所有 Python3 提交中击败了 54.29% 的用户。

3.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n) ;
  • 空间复杂度: O ( n ) O(n) O(n) 。

4. 解法三(广度优先遍历)

4.1 分析

同上述隶属于深度右边遍历范畴的前序遍历和后序遍历算法不同的是,广度优先遍历采用迭代而非递归的方式进行序列化和反序列化,广度优先遍历的实现一般会使用一个双端队列(在 Python 中,内置的 deque 即为双端队列1),以实现按层的顺序保存和遍历结点。

4.2 解答

from collections import deque
from json import dumps, loads
from typing import List, Deque, Optionalclass TreeNode(object):def __init__(self, val: int):self.val = valself.left = Noneself.right = Noneclass SerializerDeserializer:def bfs_serialize(self, root: TreeNode) -> Optional[str]:nodes = []if not isinstance(root, TreeNode):return dumps(nodes)visiting = deque()visiting.append(root)while len(visiting) > 0:node = visiting.popleft()if node is None:nodes.append(None)else:nodes.append(node.val)visiting.append(node.left)visiting.append(node.right)return dumps(nodes)def bfs_deserialize(self, data: str) -> Optional[TreeNode]:nodes = list(loads(data))if len(nodes) == 0:returnroot = TreeNode(nodes[0])parents = deque()parents.append(root)i = 1while i < len(nodes):parent = parents.popleft()left = nodes[i]i += 1if left is not None:parent.left = TreeNode(left)parents.append(parent.left)else:parent.left = Noneright = nodes[i]i += 1if right is not None:parent.right = TreeNode(right)parents.append(parent.right)else:parent.right = Nonereturn rootdef bfs_serialize_deserialize(serializer: SerializerDeserializer, root: TreeNode):serialized_tree = serializer.bfs_serialize(root)print(serialized_tree)  # [1, 2, 3, null, null, 4, 5, null, null, null, null]deserialized_root = serializer.bfs_deserialize(serialized_tree)reconstructed_tree = []def bfs_traverse(tree_root: TreeNode):if not isinstance(tree_root, TreeNode):returnvisiting = deque()visiting.append(tree_root)while len(visiting) > 0:node = visiting.popleft()if node is None:reconstructed_tree.append(None)else:reconstructed_tree.append(node.val)visiting.append(node.left)visiting.append(node.right)bfs_traverse(deserialized_root)print(reconstructed_tree)  # [1, 2, 3, None, None, 4, 5, None, None, None, None]print(dumps(reconstructed_tree))  # [1, 2, 3, null, null, 4, 5, null, null, null, null]def main():node7 = TreeNode(5)node6 = TreeNode(4)node3 = TreeNode(3)node3.left = node6node3.right = node7node2 = TreeNode(2)node1 = TreeNode(1)node1.left = node2node1.right = node3root = node1serializer = SerializerDeserializer()bfs_serialize_deserialize(serializer, root)if __name__ == '__main__':main()
  • 执行用时: 104 ms , 在所有 Python3 提交中击败了 80.35% 的用户;
  • 内存消耗: 19.7 MB , 在所有 Python3 提交中击败了 23.53% 的用户。

4.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n) ;
  • 空间复杂度: O ( n ) O(n) O(n) 。

  1. 关于双端队列的底层实现,请参考:【数据结构Python描述】队列和双端队列简介及基于列表的Python实现 和 【数据结构Python描述】双向链表简介、Python实现及应用。 ↩︎

【LeetCode 二叉树专项】二叉树的序列化与反序列化(297)相关推荐

  1. 微软面试题Leetcode 428 n叉树的序列化和反序列化

    大致思想是记录每个节点的儿子个数,自定义一个序列化协议 /* // Definition for a Node. class Node { public:int val;vector<Node* ...

  2. golang力扣leetcode 297.二叉树的序列化与反序列化

    297.二叉树的序列化与反序列化 297.二叉树的序列化与反序列化 题解 代码 297.二叉树的序列化与反序列化 297.二叉树的序列化与反序列化 题解 题目:给你一个二叉树,序列化从一个string ...

  3. LeetCode 297. 二叉树的序列化与反序列化(前序遍历层序遍历)

    文章目录 1. 题目 2. 解题 2.1 前序遍历 2.2 层序遍历 1. 题目 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过 ...

  4. 【LeetCode】【HOT】297. 二叉树的序列化与反序列化(BFS)

    [LeetCode][HOT]297. 二叉树的序列化与反序列化 文章目录 [LeetCode][HOT]297. 二叉树的序列化与反序列化 package hot;import java.util. ...

  5. Java实现 LeetCode 297 二叉树的序列化与反序列化

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

  6. 【leetcode】二叉树,297二叉树的序列化与反序列化

    297. 二叉树的序列化与反序列化 前言 题目难度:困难 一.题目描述 二.前序遍历解法 三.后序遍历解法 四.中序遍历解法 五.层级遍历解法 前言 如果你看过下面这几篇,那这道题应该对你来说信手拈来 ...

  7. leetcode:297. 二叉树的序列化与反序列化

    题目来源 leetcode 题目描述 . struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : val(0), le ...

  8. LeetCode 297. 二叉树的序列化与反序列化 | Python

    文章目录 297. 二叉树的序列化与反序列化 题目 解题思路 代码实现 实现结果 总结 297. 二叉树的序列化与反序列化 题目来源:力扣(LeetCode)https://leetcode-cn.c ...

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

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

最新文章

  1. vb 6.0服务器组件选哪个,VB 6.0包括几种版本?分别是什么?
  2. es6 语法 (Promise)
  3. 用 Flask 来写个轻博客 (21) — 结合 reCAPTCHA 验证码实现用户注册与登录
  4. jsp+servlet+mysql增删改查
  5. PHP面试题:请以空格作为间隔,拆分字符串’Apple Orange Banana Strawberry’,组成数组$fruit,
  6. 本地---tcpserver与tcpclient
  7. 超全整理|Python 操作 Excel 库 xlwings 常用操作详解!
  8. 微软收购Citus Data | 再次肯定对开源的承诺,并加速了Azure PostgreSQL的性能和扩展...
  9. 学术诚信的重要性_申论作文开头之诚信
  10. Python(18)-字典dictionary、集合
  11. 豆瓣9.0,35万读者“搜不到信息”的神秘作者,我们帮你找到了
  12. 输出以下图案菱形7行_华丽大气的手工围巾,颜值丝毫不输大牌商品,一款花式菱形围巾!...
  13. 将一个列表的奇数列赋值到另外一个列表
  14. 解决PHP导出大量数据时设置超链接的问题 --mxp
  15. codevs1700 施工方案第二季
  16. Verilog HDL中使用系统任务 $readmemh遇到问题及解决方法
  17. javascript获取TreeView控件选中节点的Text和Value
  18. 编译android模拟器,编译Android模拟器(make sdk),以及错误处理
  19. import oracle utility_oracle executing oracle import utility,please wait终极解决方案
  20. 懒癌发作,福利直接发,不抢白不抢!

热门文章

  1. HBase查询数据方式汇总
  2. CNN图像多标签分类
  3. for循环优化 --- 多线程并发实现
  4. 怎么用html.css做一个最浪漫的表白方式
  5. Unity教程||Unity添加中文字体||Unity知识记录--制作UI粒子特效
  6. 物联网--Zigbee协议(一):Zigbee初识
  7. 快速定位到函数指针所对应的函数
  8. 罗素悖论-图灵停机问题
  9. 感恩与您同行,迈向美好未来 | 迅镭激光第二季度生日会&迎新会成功举办!
  10. Android SurfaceView 的使用