Framework Thinking - Neetcode 150 - Serialize and Deserialize Binary Tree

Here is solutions for Serialize and Deserialize Binary Tree.

1. Understand the problem

You are given a binary tree. You must:

  • Serialize it: convert the tree into a string.

  • Deserialize it: convert that string back into the same original tree structure.

2. Clarify constraints, asks 4 - 5 questions including edge cases.

  1. Can node values be negative or large?
  • Yes — assume any 32-bit integer.
  1. Can the tree be empty?
  • Yes — root = None must be handled.
  1. Is the tree guaranteed to be a BST?
  • No — it’s a general binary tree.
  1. Do we control the output format of serialization?
  • Yes — we can design any format.
  1. Do duplicate values appear?
  • Yes — so structure matters, not just values.

3. Explore examples.

  1. Example 1:
      1
     / \
    2   3
       / \
      4   5

  • Serialization:
"1,2,#,#,3,4,#,#,5,#,#"
  1. Example 2:
1
  • Serialization:
"1,#,#"
  1. Example 3:
None
  • Serialization:
"#"

4. Brainstorm 2 - 3 solutions, naive solution first and optimize later. Explain the key idea of each solution.

4.1. Solution 1 — Preorder DFS with Null Markers (Most Common) - Time O(N), Space O(N)

  • Key Idea

    • Use preorder traversal (Node → Left → Right):

    • When you see a real node → write its value.

    • When you see None → write a special marker like “#”.

  • Time: O(N).

  • Space: O(N).

4.2. Solution 2 - Level Order (BFS) Serialization - Time O(N), Space: O(N)

1,2,3,#,#,4,5
  • Time: O(N).

  • Space: O(N).

4.3. Solution 3 - Optimize for BST - Time O(N), Space O(N) but do not need to store NULL

  • A Binary Search Tree has the strict rule:
left subtree < root < right subtree
  • So in a BST:

    • Preorder traversal = root is always the first value
    • All following values smaller than root must belong to the left subtree
    • All larger values must belong to the right subtree
    • Do not need to store NULL but you can reorder it by value.
  • Time: O(N).

  • Space O(N).

5. Implement solutions.

5.1. DFS - Time O(N), Space O(H)

  • Idea: It is completed binary tree.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Codec:

    # Encodes a tree to a single string.
    def serialize(self, root: Optional[TreeNode]) -> str:
        res = []

        def dfs(node):
            if not node:
                res.append("#")
                return
            res.append(str(node.val))
            dfs(node.left)
            dfs(node.right)

        dfs(root)
        return ",".join(res)

    # Decodes your encoded data to tree.
    def deserialize(self, data: str) -> Optional[TreeNode]:
        vals = data.split(",")
        self.i = 0

        def dfs():
            if vals[self.i] == "#":
                self.i += 1
                return None
            node = TreeNode(int(vals[self.i]))
            self.i += 1
            node.left = dfs()
            node.right = dfs()
            return node

        return dfs()
  • Time: O(N).

  • Space O(H).

5.2. BFS - Time O(N), Space O(N)

  • Idea: It is completed binary tree.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Codec:

    # Encodes a tree to a single string.
    def serialize(self, root: Optional[TreeNode]) -> str:
        if not root:
            return "N"
        res = []
        queue = deque([root])
        while queue:
            node = queue.popleft()
            if not node:
                res.append("N")
            else:
                res.append(str(node.val))
                queue.append(node.left)
                queue.append(node.right)
        return ",".join(res)

    # Decodes your encoded data to tree.
    def deserialize(self, data: str) -> Optional[TreeNode]:
        vals = data.split(",")
        if vals[0] == "#":
            return None
        root = TreeNode(int(vals[0]))
        queue = deque([root])
        index = 1
        while queue:
            node = queue.popleft()
            if vals[index] != "#":
                node.left = TreeNode(int(vals[index]))
                queue.append(node.left)
            index += 1
            if vals[index] != "#":
                node.right = TreeNode(int(vals[index]))
                queue.append(node.right)
            index += 1
        return root
  • Time: O(N).

  • Space O(N).

6. Dry run testcases.

      1
     / \
    2   3
       / \
      4   5

Step Node Action Output List
1 1 add value ["1"]
2 2 add value ["1","2"]
3 None add # ["1","2","#"]
4 None add # ["1","2","#","#"]
5 3 add value ["1","2","#","#","3"]
6 4 add value ["1","2","#","#","3","4"]
7 None add # ["1","2","#","#","3","4","#"]
8 None add # ["1","2","#","#","3","4","#","#"]
9 5 add value ["1","2","#","#","3","4","#","#","5"]
10 None add # ["1","2","#","#","3","4","#","#","5","#"]
11 None add # ["1","2","#","#","3","4","#","#","5","#","#"]
December 4, 2025