在问 · 算法题解

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

中等设计先序遍历 + 上下界递归重建力扣原题 ↗

题目描述

设计一个 Codec 类,负责二叉搜索树与字符串之间的相互转换。

serialize(root):把以 root 为根的二叉搜索树编码为一个字符串并返回。deserialize(data):把 serialize 得到的字符串还原成树,返回根节点。

编码格式由你自己决定,只要还原后的树和原树完全一致即可。编码结果应当尽量短。

测试时会先编码、再解码,比较得到的树。树用层序数组表示,空树为 []。

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

解释:编码再解码后得到与原树相同的 [2,1,3]。

示例 2
输入:root = []
输出:[]

解释:空树编码后还原仍是空树。

数据范围

  • 节点个数在 [0, 10^4] 范围内。
  • 0 <= Node.val <= 10^4
  • 输入一定是一棵二叉搜索树。

解题思路:先序遍历 + 上下界递归重建

二叉搜索树的前序序列已经能唯一确定这棵树,所以不需要空位记号,只写出节点值即可。反序列化时,给每次 build 调用一个开区间 (lo, hi):如果下一个值落在区间内,它就是这棵子树的根,消费它后用 (lo, 根) 建左子树、用 (根, hi) 建右子树;否则这棵子树为空,值留给祖先的右子树。

为什么正确

build(lo, hi) 被调用时,vals[i] 之前的值都已挂到树上;这棵子树恰好由从 i 开始、连续落在 (lo, hi) 内的那一段前序值组成。

复杂度分析

时间 O(n),每个值只被消费一次;空间 O(n),递归深度为树高。

易错点

区间必须是开区间,并且向下传递时要同时收紧上下界。只比较父节点大小会把值挂错位置。另外空树序列化为空字符串,反序列化时要先过滤掉空串,不能把它当数字解析。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Codec:
    def serialize(self, root):
        out = []
        def dfs(node):
            if not node:
                return
            out.append(str(node.val))
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        return ','.join(out)

    def deserialize(self, data):
        vals = [int(x) for x in data.split(',') if x]
        i = 0
        def build(lo, hi):
            nonlocal i
            if i == len(vals) or not (lo < vals[i] < hi):
                return None
            node = TreeNode(vals[i])
            i += 1
            node.left = build(lo, node.val)
            node.right = build(node.val, hi)
            return node
        return build(float('-inf'), float('inf'))

载入编辑器,自己跑一遍 →

其他解法

上下界递归直接用 BST 性质划分区间,代码短;单调栈是迭代写法,避免深递归。

解法 2:先序遍历 + 单调栈重建 时间 O(n),空间 O(n)

序列化仍输出先序序列,用显式栈遍历。反序列化时维护一个从底到顶值递减的栈:新值小于栈顶就作为栈顶的左孩子;否则不断弹出比它小的节点,最后弹出的那个就是它的父节点,新值作为父节点的右孩子。每个新节点都要入栈。

全程不递归,树退化成链时也不会栈溢出。需要理解单调栈为什么能找到父节点,不如上下界递归直观。

class Codec:
    def serialize(self, root):
        # 用显式栈做先序遍历
        out = []
        stack = [root] if root else []
        while stack:
            node = stack.pop()
            out.append(str(node.val))
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return ','.join(out)

    def deserialize(self, data):
        vals = [int(x) for x in data.split(',') if x]
        if not vals:
            return None
        root = TreeNode(vals[0])
        stack = [root]  # 栈中节点值从底到顶递减
        for v in vals[1:]:
            node = TreeNode(v)
            if v < stack[-1].val:
                stack[-1].left = node
            else:
                parent = None
                while stack and stack[-1].val < v:
                    parent = stack.pop()
                parent.right = node
            stack.append(node)
        return root

设计 专题的其他题目

查看 设计 全部题目 →