Serialize and Deserialize BST

Sanjeev SharmaSanjeev Sharma
3 min read

Advertisement

Problem

Design an algorithm to serialize and deserialize a BST. The encoded string should be as compact as possible.

Key insight (BST): Preorder traversal uniquely defines a BST. During deserialization, use min/max bounds to place each value correctly — no need to store null markers.

Solutions

// C++
string serialize(TreeNode* root) {
    if (!root) return "";
    string s = to_string(root->val) + " ";
    s += serialize(root->left);
    s += serialize(root->right);
    return s;
}
TreeNode* deserialize(string data) {
    istringstream iss(data);
    queue<int> q;
    int v;
    while (iss >> v) q.push(v);
    function<TreeNode*(int,int)> build = [&](int mn, int mx) -> TreeNode* {
        if (q.empty() || q.front() < mn || q.front() > mx) return nullptr;
        auto node = new TreeNode(q.front()); q.pop();
        node->left = build(mn, node->val);
        node->right = build(node->val, mx);
        return node;
    };
    return build(INT_MIN, INT_MAX);
}
// Java
public String serialize(TreeNode root) {
    if (root == null) return "";
    return root.val + " " + serialize(root.left) + serialize(root.right);
}
public TreeNode deserialize(String data) {
    if (data.isEmpty()) return null;
    String[] parts = data.trim().split(" ");
    Queue<Integer> q = new LinkedList<>();
    for (String p : parts) q.add(Integer.parseInt(p));
    return build(q, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
TreeNode build(Queue<Integer> q, int min, int max) {
    if (q.isEmpty() || q.peek() < min || q.peek() > max) return null;
    TreeNode node = new TreeNode(q.poll());
    node.left = build(q, min, node.val);
    node.right = build(q, node.val, max);
    return node;
}
// JavaScript
function serialize(root) {
    if (!root) return '';
    return root.val + ' ' + serialize(root.left) + serialize(root.right);
}
function deserialize(data) {
    if (!data) return null;
    const vals = data.trim().split(' ').map(Number);
    let i = 0;
    function build(min, max) {
        if (i >= vals.length || vals[i] < min || vals[i] > max) return null;
        const node = new TreeNode(vals[i++]);
        node.left = build(min, node.val);
        node.right = build(node.val, max);
        return node;
    }
    return build(-Infinity, Infinity);
}
# Python
def serialize(root):
    def preorder(node):
        if not node:
            return []
        return [node.val] + preorder(node.left) + preorder(node.right)
    return ' '.join(map(str, preorder(root)))

def deserialize(data):
    if not data:
        return None
    vals = iter(map(int, data.split()))
    def build(mn, mx):
        val = next(vals, None)
        if val is None or val < mn or val > mx:
            return None
        # Problem: consumed the val; need to peek
        # Use a list and index instead
        return None
    # Better iterative approach with index
    vals = list(map(int, data.split()))
    idx = [0]
    def build2(mn, mx):
        if idx[0] >= len(vals) or vals[idx[0]] < mn or vals[idx[0]] > mx:
            return None
        node = TreeNode(vals[idx[0]])
        idx[0] += 1
        node.left = build2(mn, node.val)
        node.right = build2(node.val, mx)
        return node
    return build2(float('-inf'), float('inf'))

Complexity

  • Time: O(n) serialize, O(n) deserialize
  • Space: O(n)

Key Insight

Preorder + BST property is sufficient for unique reconstruction. No null markers needed, saving space.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro