序列化和反序列化二叉搜索树

61 阅读1分钟

838. 序列化和反序列化二叉搜索树 - 力扣(LeetCode)

序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。

设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。

编码的字符串应尽可能紧凑。

示例 1:

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

示例 2:

输入: root = []
输出: []

提示:

  • 树中节点数范围是 [0, 10^4]
  • 0 <= Node.val <= 10^4
  • 题目数据 保证 输入的树是一棵二叉搜索树。

思路

我们以下图的搜索二叉树为例说明

广度优先

因为是二叉树,每个根节点最多有两个子节点,序列化时可以按层级遍历二叉树,当一个节点缺少左节点或右节点时,我们可以用一个二叉树不存在的值来填充,序列化后的数组为[8, 4, 10, 2,6, 9, 12, 1, 3, 5, 7, null, null, null, 11]。反序列化时,我们第 i 层节点最多有 2^i 个节点,第 n 个节点,它的左节点是第 2n + 1 个节点,右节点是 2n + 2 个节点。知道了上面的关系,我们就可反序列化了。

前序遍历

因为是搜索二叉树,我们可以用前序遍历或后续遍历进行二叉树的序列化和反序列化,以前序遍历为例,序列化后的数组为[8, 4, 2, 1, 3, 6, 5, 7, 10, 9, 12, 11],反序列化时,我们知道第一个值是跟节点,即8是跟节点,左子树都小于8, 右子树都大于8,我们可以遍历找到第一个大于8的值的位置并分割数组,[4, 2, 1, 3, 6, 5, 7]为左子树,[10, 9, 12, 11]为右子树,再分别对左右子树进行相同的操作,直到没有子树为止。

解题

/**
 * Encodes a tree to a single string.
 *
 * @param {TreeNode} root
 * @return {string}
 */
var serialize = function (root) {
  const arr = [];
  const dfs = (root) => {
    if (!root) {
      return;
    }
    arr.push(root.val);
    dfs(root.left);
    dfs(root.right);
  };
  dfs(root);
  return arr;
};

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function (data) {
  if (!data?.length) return null;
  const bf = (left, right, val) => {
    while (left <= right) {
      const mid = (left + right) >> 1;
      if (data[mid] >= val) {
        right = mid - 1;
      } else {
        left = mid + 1;
      }
    }
    return left;
  };
  const dfs = (nums, left, right) => {
    const val = nums[left];
    const node = new TreeNode(val);
    if (right > left) {
      const idx = bf(left + 1, right, val);
      if(idx>left+1){
        node.left = dfs(nums, left+1, idx-1)
      }
      if(idx<right+1){
        node.right = dfs(nums, idx, right)
      }
    }
    return node
  };
  return dfs(data, 0, data.length-1)
};