Algorithm

Serialize and Deserialize Binary Tree

Trees Pattern

Serialize and Deserialize Binary Tree

Design two functions. serialize(root) turns a binary tree into a single string; deserialize(data) turns such a string back into a tree that is structurally identical to the original, with the same values in the same positions. The string format is entirely your choice — you are only required to be able to read back what you wrote. An empty tree must survive the round trip too. This is an ordinary binary tree: no ordering of values may be assumed.

CONSTRAINTS
  • The number of nodes in the tree is in the range [0, 10⁴]
  • -1000 <= Node.val <= 1000
  • Values may repeat, and may be negative
  • You choose the encoding; only the round trip is graded
EXAMPLE 1
Input: root = [1,2,3,null,null,4,5]
Output: round trip reproduces [1,2,3,null,null,4,5]
The rebuilt tree must match node for node, including which children are missing — 2 has none while 3 has two.
EXAMPLE 2
Input: root = []
Output: round trip reproduces []
The empty tree has to serialize to something the reader recognises as 'no tree', not to an empty string that could be mistaken for missing data.
EXAMPLE 3
Input: root = [1,2,null,3]
Output: round trip reproduces [1,2,null,3]
3 hangs on the left of 2, and 2 on the left of 1. An encoding that loses which side a lone child is on would rebuild the wrong tree here.
EXAMPLE 4
Input: root = [7,7,7]
Output: round trip reproduces [7,7,7]
Repeated values are legal, so the encoding cannot rely on values being distinct — a restriction the two reconstruction problems did depend on.
Is the string format fixed?
No, it is yours to choose. Only the round trip is checked, which is why this problem is really about designing an unambiguous encoding.
Can values repeat here?
Yes, unlike in the two-traversal reconstruction problems. Any approach that identifies nodes by value is therefore unavailable.
Must the empty tree be handled?
Yes. Decide up front what a null root serialises to, and make sure the reader treats it as an empty tree rather than as malformed input.
How do I keep negative numbers from breaking the format?
Use a separator that cannot appear inside a value — a comma is fine, since a minus sign only ever leads a token. Fixing that boundary is why splitting on a delimiter beats reading character by character.

A tree is a 2-D shape; a string is a single line. Serialize flattens the tree into a string; deserialize reads it back into the same tree. The only real question: what does "lose nothing" take?

A plain traversal loses the shape

Hand over the pre-order list [1, 2]: 1 is the root, 2 its only child — but which side?

text
   1              1
  /       or       \
 2                  2

  both give pre-order [1, 2]

The information that vanished is the missing child. A traversal lists only the nodes that exist and says nothing about the empty sides — so the reader can't tell the two shapes apart.

Write the gaps down too

Emit a marker — say # — for every null link. Now the two shapes become different strings:

text
   1     ->   1,2,#,#,#      (2 on the left; 2's two nulls; then 1's right null)
  /
 2

   1     ->   1,#,2,#,#      (1's left null; then 2 with its two nulls)
    \
     2

With nulls included, a single pre-order string rebuilds the tree with zero guessing. Every token is a complete instruction: a value means "make a node — the next tokens are its whole left subtree, then its whole right subtree"; a # means "empty here, consume nothing more." So the reader just scans left to right, no lookahead.

python
def serialize(node):
    if node is None:
        return "#"                                  # the gap is data too
    return f"{node.val},{serialize(node.left)},{serialize(node.right)}"

def deserialize(data):
    tokens = iter(data.split(","))                  # read strictly left to right

    def build():
        tok = next(tokens)                          # consume exactly one token
        if tok == "#":
            return None                             # empty subtree — nothing more to read
        node = Node(int(tok))
        node.left  = build()                        # next tokens = the whole left subtree
        node.right = build()                        # then the whole right subtree
        return node

    return build()

Notice the mirror: the writer recurses node, left, right; the reader recurses node, left, right. Same shape — the reader is just replaying the writer's walk.

Crucial Notebuild must consume its token before recursing, and assign left then right. The stream carries no positions — only order — so correctness rests entirely on reading tokens in the exact order they were written. Swap the two lines and you get a valid-looking but silently mirrored tree.
Watch the round trip

Tree: root 1, children 2 and 3; 3's children 4 and 5.

text
serialize (pre-order + nulls):

        1
       / \        ->   1, 2,#,#, 3, 4,#,#, 5,#,#
      2   3
         / \
        4   5

deserialize, token by token:
  1  -> node 1, build its left...
  2  -> node 2; left #=null, right #=null  -> leaf, becomes 1.left
  3  -> node 3, build its left...
  4  -> #, # -> leaf 4  (3.left)
  5  -> #, # -> leaf 5  (3.right)
  stream empty -> tree complete

Eleven tokens for five nodes — six of them markers. That's the price, and it's cheap.

The idea that generalises

Every node written once, every null link written once. A tree with N nodes has N + 1 null links, so the string is 2N + 1 tokens: O(N) time and output; deserialize is O(N) too, O(H) recursion depth. (A level-order/BFS encoding with markers works equally well — that's the familiar LeetCode array format.)

The transferable principle reaches well past trees: you can flatten a structure into a line without loss exactly when the reader can tell where each part ends. Either send a second view that pins the boundaries, or write explicit terminators so each part announces its own end (the # markers here). Length prefixes are a third option — and JSON, protobuf, and network protocols all pick one of the three. Designing any encoding, ask first: what tells the reader where this piece stops?

Interactive Strategy Visualization

Object Persistence

Serialization & Deserialization Pipeline

BINARY TREE
SERIAL STREAM
1,2,x,x,3,4,x,x,5
RECONSTRUCTED
PREORDER STRATEGY
Use recursive Preorder to ensure the root is always written/read before its children.
NULL MARKING
Explicitly mark empty children (usually with 'null' or 'x') to preserve the tree structure without needing extra traversals.
Ambiguous Without Null Markers
O(N) Time · O(N) Space Pre-order With Null Markers
O(N) Time · O(N) Space Level-order Encoding