1. 题目描述

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

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

示例:

你可以将以下二叉树:

  1. 1
  2. / \
  3. 2 3
  4. / \
  5. 4 5
  6. 序列化为 "[1,2,3,null,null,4,5]"

提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。

2. 解题思路

对于这道二叉树的题目,我们能想到的最直接的方式就是深度优先宾利和广度优先遍历,这里就分别用两种方式来解答。

深度优先遍历:

  • 首先是序列化二叉树,可以定义一个遍历方法,先访问根节点,再访问左节点,最后访问右节点,将每个节点的值都存入数组,如果是null也要存入数组。
  • 之后是反序列化二叉树,也就是将数组转化为二叉树,因为数组是二叉树先序遍历的结果,所以我们就可以遍历数组,然后按照根节点、左子树、右子树的顺序复原二叉树

使用深度优先遍历的方法的时间复杂度为O(n),空间复杂度为O(n)。

广度优先遍历:

  • 首先是序列化二叉树,广度优先遍历的遍历顺序是按照层级从上往下遍历(层序遍历),所以我们可以利用队列先进先出的特性,维持一个数组。先将根节点入队,再将左节点和右节点入队,递归即可。
  • 之后是反序列化二叉树,我们可以从数组中取出第一个元素生成根节点,将根节点加入队列,循环队列,将根节点的左右子树分别加入队列,循环此操作,直至队列为空。其中队列中的节点用于后面遍历其左右子节点。

使用广度优先遍历的方法的时间复杂度为O(n),空间复杂度为O(n)。

2. 代码实现

深度优先遍历:

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val) {
  4. * this.val = val;
  5. * this.left = this.right = null;
  6. * }
  7. */
  8. /**
  9. * Encodes a tree to a single string.
  10. *
  11. * @param {TreeNode} root
  12. * @return {string}
  13. */
  14. var serialize = function(root) {
  15. const result = []
  16. function traverseNode(node){
  17. if(node === null){
  18. result.push(null)
  19. }else{
  20. result.push(node.val)
  21. traverseNode(node.left)
  22. traverseNode(node.right)
  23. }
  24. }
  25. traverseNode(root)
  26. return result
  27. };
  28. /**
  29. * Decodes your encoded data to tree.
  30. *
  31. * @param {string} data
  32. * @return {TreeNode}
  33. */
  34. var deserialize = function(data) {
  35. const len = data.length
  36. if(!len){
  37. return null
  38. }
  39. let i = 0
  40. function structure (){
  41. // 递归停止条件
  42. if(i >= len){
  43. return null
  44. }
  45. const val = data[i]
  46. i++
  47. if(val === null){
  48. return null
  49. }
  50. const node = new TreeNode(val)
  51. node.left = structure()
  52. node.right = structure()
  53. return node
  54. }
  55. return structure()
  56. };
  57. /**
  58. * Your functions will be called as such:
  59. * deserialize(serialize(root));
  60. */

广度优先遍历:

  1. /**
  2. * Definition for a binary tree node.
  3. * function TreeNode(val) {
  4. * this.val = val;
  5. * this.left = this.right = null;
  6. * }
  7. */
  8. /**
  9. * Encodes a tree to a single string.
  10. *
  11. * @param {TreeNode} root
  12. * @return {string}
  13. */
  14. var serialize = function(root) {
  15. if(!root){
  16. return []
  17. }
  18. const result = []
  19. const queue = []
  20. queue.push(root)
  21. let node ;
  22. while(queue.length){
  23. node = queue.shift()
  24. result.push(node ? node.val : null)
  25. if(node){
  26. queue.push(node.left)
  27. queue.push(node.right)
  28. }
  29. }
  30. return result
  31. };
  32. /**
  33. * Decodes your encoded data to tree.
  34. *
  35. * @param {string} data
  36. * @return {TreeNode}
  37. */
  38. var deserialize = function(data) {
  39. const len = data.length
  40. if(!len){
  41. return null
  42. }
  43. const root = new TreeNode(data.shift())
  44. const queue = [root]
  45. while(queue.length){
  46. // 取出将要遍历的节点
  47. const node = queue.shift()
  48. if(!data.length){
  49. break
  50. }
  51. // 还原左节点
  52. const leftVal = data.shift()
  53. if(leftVal === null){
  54. node.left = null
  55. }else{
  56. node.left = new TreeNode(leftVal)
  57. queue.push(node.left)
  58. }
  59. if(!data.length){
  60. break
  61. }
  62. // 还原右节点
  63. const rightVal = data.shift()
  64. if(rightVal === null){
  65. node.right = null
  66. }else{
  67. node.right = new TreeNode(rightVal)
  68. queue.push(node.right)
  69. }
  70. }
  71. return root
  72. };
  73. /**
  74. * Your functions will be called as such:
  75. * deserialize(serialize(root));
  76. */

4. 提交结果

深度优先遍历:
297. 二叉树的序列化与反序列化 - 图1
广度优先遍历:
297. 二叉树的序列化与反序列化 - 图2