Description
Serialize and Deserialize Binary Tree
Serialization is the process of 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 tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Example 1:

Input: root = [1,2,3,null,null,4,5]
Output: [1,2,3,null,null,4,5]
Example 2:
Input: root = []
Output: []
Constraints:
- The number of nodes in the tree is in the range
[0, 104]. -1000 <= Node.val <= 1000
Primary Approach: Preorder DFS with Sentinel Values ( Time, Space)
Intuition
Standard Preorder traversal () alone cannot uniquely identify a binary tree because structure is lost. However, if we explicitly include null markers (sentinels) in the traversal, the tree structure becomes 100% deterministic and easy to reconstruct:
- Serialization: Perform Preorder DFS. For a non-null node, append its value followed by a delimiter (
,). Fornullnodes, append a sentinel character like"N,". - Deserialization: Split the string by delimiter into a global
Queue. Because Preorder processes , popping the queue sequentially provides the exact order needed to recursively build the root, left subtree, and right subtree.
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
public class Codec {
private static final String NULL_MARKER = "N";
private static final String DELIMITER = ",";
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
buildString(root, sb);
return sb.toString();
}
private void buildString(TreeNode node, StringBuilder sb) {
if (node == null) {
sb.append(NULL_MARKER).append(DELIMITER);
return;
}
// Preorder DFS: Root -> Left -> Right
sb.append(node.val).append(DELIMITER);
buildString(node.left, sb);
buildString(node.right, sb);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
// Queue<String> nodes = new LinkedList<>(Arrays.asList(data.split(DELIMITER))); old
Queue<String> nodes = new ArrayDeque<>(Arrays.asList(data.split(DELIMITER)));
return buildTree(nodes);
}
private TreeNode buildTree(Queue<String> nodes) {
String val = nodes.poll();
if (val == null || val.equals(NULL_MARKER)) {
return null;
}
TreeNode node = new TreeNode(Integer.parseInt(val));
node.left = buildTree(nodes);
node.right = buildTree(nodes);
return node;
}
}
Complexity
- Time Complexity: — Every node (and
nullleaf pointer) is visited once during both encoding and decoding. - Space Complexity: — The output string stores elements (nodes + null markers). The recursion call stack and
Queuetake space in the worst case.
Alternative Approach: Level-Order BFS ( Time, Space)
Intuition
This mirrors how LeetCode formats binary tree test cases internally using Breadth-First Search:
- Serialize: Process nodes level by level using a
Queue. Appendnode.valor"N"to aStringBuilder, pushingnode.leftandnode.rightinto the queue regardless of whether they arenull. - Deserialize: Read the array of values starting with
root = values[0]. Use aQueueto attach left childvalues[i]and right childvalues[i+1]to each parent popped from the queue.
import java.util.LinkedList;
import java.util.Queue;
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "";
StringBuilder sb = new StringBuilder();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node == null) {
sb.append("N,");
continue;
}
sb.append(node.val).append(",");
queue.add(node.left);
queue.add(node.right);
}
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data == null || data.isEmpty()) return null;
String[] values = data.split(",");
TreeNode root = new TreeNode(Integer.parseInt(values[0]));
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
for (int i = 1; i < values.length; i++) {
TreeNode parent = queue.poll();
// Process Left Child
if (!values[i].equals("N")) {
TreeNode left = new TreeNode(Integer.parseInt(values[i]));
parent.left = left;
queue.add(left);
}
// Process Right Child
i++;
if (!values[i].equals("N")) {
TreeNode right = new TreeNode(Integer.parseInt(values[i]));
parent.right = right;
queue.add(right);
}
}
return root;
}
}
Complexity
- Time Complexity: — Processes every node and null marker in level-order sequence.
- Space Complexity: — BFS queue holds up to nodes ( at the bottom level) plus the string representation of nodes.
Key Interview Discussion Points
- Use
StringBuilderover String Concatenation: Always useStringBuilderin Java. Repeated string concatenation (s += val) takes time due to string immutability, whileStringBuilder.append()operates in time. - Why Preorder DFS is Preferred: While BFS is intuitive, Preorder DFS requires significantly cleaner code (~25 lines vs ~45 lines) with fewer boundary checks during deserialization.
- BST Optimization Follow-Up (LeetCode 449): If told the input tree is a Binary Search Tree, emphasize that
nullmarkers are unnecessary! A standard preorder traversal can be reconstructed into a BST in time using standard dynamic range bounds(min, max).
Easy Memory Rule
“Preorder DFS () + Null Markers (
N) Store in Queue Reconstruct recursively!”