Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Move from brute-force thinking to an efficient approach using tree strategy.
Serialization is converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary search tree. There is no restriction on how your serialization/deserialization algorithm should work. You need to ensure that a binary search tree can be serialized to a string, and this string can be deserialized to the original tree structure.
The encoded string should be as compact as possible.
Example 1:
Input: root = [2,1,3] Output: [2,1,3]
Example 2:
Input: root = [] Output: []
Constraints:
[0, 104].0 <= Node.val <= 104Problem summary: Serialization is converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary search tree. There is no restriction on how your serialization/deserialization algorithm should work. You need to ensure that a binary search tree can be serialized to a string, and this string can be deserialized to the original tree structure. The encoded string should be as compact as possible.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree · Design
[2,1,3]
[]
serialize-and-deserialize-binary-tree)find-duplicate-subtrees)serialize-and-deserialize-n-ary-tree)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #449: Serialize and Deserialize BST
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
private int i;
private List<String> nums;
private final int inf = 1 << 30;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
nums = new ArrayList<>();
dfs(root);
return String.join(" ", nums);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data == null || "".equals(data)) {
return null;
}
i = 0;
nums = Arrays.asList(data.split(" "));
return dfs(-inf, inf);
}
private void dfs(TreeNode root) {
if (root == null) {
return;
}
nums.add(String.valueOf(root.val));
dfs(root.left);
dfs(root.right);
}
private TreeNode dfs(int mi, int mx) {
if (i == nums.size()) {
return null;
}
int x = Integer.parseInt(nums.get(i));
if (x < mi || x > mx) {
return null;
}
TreeNode root = new TreeNode(x);
++i;
root.left = dfs(mi, x);
root.right = dfs(x, mx);
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// String tree = ser.serialize(root);
// TreeNode ans = deser.deserialize(tree);
// return ans;
// Accepted solution for LeetCode #449: Serialize and Deserialize BST
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
type Codec struct {
}
func Constructor() Codec {
return Codec{}
}
// Serializes a tree to a single string.
func (this *Codec) serialize(root *TreeNode) string {
if root == nil {
return ""
}
data := &strings.Builder{}
var dfs func(*TreeNode)
dfs = func(root *TreeNode) {
if root == nil {
return
}
data.WriteString(strconv.Itoa(root.Val))
data.WriteByte(' ')
dfs(root.Left)
dfs(root.Right)
}
dfs(root)
return data.String()[0 : data.Len()-1]
}
// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {
if data == "" {
return nil
}
vals := strings.Split(data, " ")
i := 0
var dfs func(int, int) *TreeNode
dfs = func(mi, mx int) *TreeNode {
if i == len(vals) {
return nil
}
x, _ := strconv.Atoi(vals[i])
if x < mi || x > mx {
return nil
}
i++
root := &TreeNode{Val: x}
root.Left = dfs(mi, x)
root.Right = dfs(x, mx)
return root
}
return dfs(math.MinInt64, math.MaxInt64)
}
/**
* Your Codec object will be instantiated and called as such:
* ser := Constructor()
* deser := Constructor()
* tree := ser.serialize(root)
* ans := deser.deserialize(tree)
* return ans
*/
# Accepted solution for LeetCode #449: Serialize and Deserialize BST
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Codec:
def serialize(self, root: Optional[TreeNode]) -> str:
"""Encodes a tree to a single string."""
def dfs(root: Optional[TreeNode]):
if root is None:
return
nums.append(root.val)
dfs(root.left)
dfs(root.right)
nums = []
dfs(root)
return " ".join(map(str, nums))
def deserialize(self, data: str) -> Optional[TreeNode]:
"""Decodes your encoded data to tree."""
def dfs(mi: int, mx: int) -> Optional[TreeNode]:
nonlocal i
if i == len(nums) or not mi <= nums[i] <= mx:
return None
x = nums[i]
root = TreeNode(x)
i += 1
root.left = dfs(mi, x)
root.right = dfs(x, mx)
return root
nums = list(map(int, data.split()))
i = 0
return dfs(-inf, inf)
# Your Codec object will be instantiated and called as such:
# Your Codec object will be instantiated and called as such:
# ser = Codec()
# deser = Codec()
# tree = ser.serialize(root)
# ans = deser.deserialize(tree)
# return ans
// Accepted solution for LeetCode #449: Serialize and Deserialize BST
use rustgym_util::*;
use std::iter::Peekable;
use std::vec::IntoIter;
struct Codec;
enum Tok {
Op(char),
Num(i32),
}
impl Codec {
fn new() -> Self {
Codec {}
}
fn serialize(&self, root: TreeLink) -> String {
let mut res = "".to_string();
Self::serialize_tree(&root, &mut res);
res
}
fn serialize_tree(root: &TreeLink, s: &mut String) {
s.push('(');
if let Some(node) = root {
let node = node.borrow();
*s += &format!("{}", node.val);
Self::serialize_tree(&node.left, s);
Self::serialize_tree(&node.right, s);
}
s.push(')');
}
fn deserialize(&self, data: String) -> TreeLink {
let tokens = Self::parse_tokens(data);
let mut it = tokens.into_iter().peekable();
Self::parse_tree(&mut it)
}
fn parse_tokens(data: String) -> Vec<Tok> {
let mut it = data.chars().peekable();
let mut res = vec![];
while let Some(c) = it.next() {
if c == '(' || c == ')' {
res.push(Tok::Op(c));
} else {
let mut x = (c as u8 - b'0') as i32;
while let Some('0'..='9') = it.peek() {
x *= 10;
x += (it.next().unwrap() as u8 - b'0') as i32;
}
res.push(Tok::Num(x));
}
}
res
}
fn parse_tree(it: &mut Peekable<IntoIter<Tok>>) -> TreeLink {
let mut res = None;
it.next();
match it.peek() {
Some(&Tok::Num(x)) => {
it.next();
res = tree!(x, Self::parse_tree(it), Self::parse_tree(it))
}
Some(Tok::Op(')')) => {}
_ => panic!(),
}
it.next();
res
}
}
#[test]
fn test() {
let codec = Codec::new();
let root = tree!(2, tree!(1), tree!(3));
let ans = tree!(2, tree!(1), tree!(3));
assert_eq!(codec.deserialize(codec.serialize(root)), ans);
}
// Accepted solution for LeetCode #449: Serialize and Deserialize BST
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #449: Serialize and Deserialize BST
// /**
// * Definition for a binary tree node.
// * public class TreeNode {
// * int val;
// * TreeNode left;
// * TreeNode right;
// * TreeNode(int x) { val = x; }
// * }
// */
// public class Codec {
// private int i;
// private List<String> nums;
// private final int inf = 1 << 30;
//
// // Encodes a tree to a single string.
// public String serialize(TreeNode root) {
// nums = new ArrayList<>();
// dfs(root);
// return String.join(" ", nums);
// }
//
// // Decodes your encoded data to tree.
// public TreeNode deserialize(String data) {
// if (data == null || "".equals(data)) {
// return null;
// }
// i = 0;
// nums = Arrays.asList(data.split(" "));
// return dfs(-inf, inf);
// }
//
// private void dfs(TreeNode root) {
// if (root == null) {
// return;
// }
// nums.add(String.valueOf(root.val));
// dfs(root.left);
// dfs(root.right);
// }
//
// private TreeNode dfs(int mi, int mx) {
// if (i == nums.size()) {
// return null;
// }
// int x = Integer.parseInt(nums.get(i));
// if (x < mi || x > mx) {
// return null;
// }
// TreeNode root = new TreeNode(x);
// ++i;
// root.left = dfs(mi, x);
// root.right = dfs(x, mx);
// return root;
// }
// }
//
// // Your Codec object will be instantiated and called as such:
// // Codec ser = new Codec();
// // Codec deser = new Codec();
// // String tree = ser.serialize(root);
// // TreeNode ans = deser.deserialize(tree);
// // return ans;
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.