Bird
Raised Fist0
Interview Preptree-dfshardFacebookAmazonGoogleMicrosoft

Serialize and Deserialize Binary Tree

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
📋
Problem

Imagine you want to save a complex family tree to a file and later reconstruct it exactly as it was. How do you convert the tree into a string and then back into the original tree structure?

Design an algorithm to serialize a binary tree into a string and deserialize the string back to the original binary tree structure. Serialization converts the tree into a string representation that can be stored or transmitted, and deserialization reconstructs the tree from that string. The tree nodes contain integer values. Implement two functions: serialize(root) which returns a string, and deserialize(data) which returns the root of the tree.

The number of nodes in the tree is in the range [0, 10^5].Node values are integers and can be negative or positive.The serialization and deserialization must be lossless and efficient.
Edge cases: Empty tree → should serialize to an empty string or a special marker and deserialize to nullTree with only left children → serialization must correctly represent null right childrenTree with only right children → serialization must correctly represent null left children
</>
IDE
def serialize(root: Optional[TreeNode]) -> str: pass def deserialize(data: str) -> Optional[TreeNode]: passpublic String serialize(TreeNode root) { // ... } public TreeNode deserialize(String data) { // ... }string serialize(TreeNode* root) { // ... } TreeNode* deserialize(const string& data) { // ... }function serialize(root) { // ... } function deserialize(data) { // ... }
def serialize(root):
    # Write your solution here
    pass

def deserialize(data):
    # Write your solution here
    pass
class Codec {
    public String serialize(TreeNode root) {
        // Write your solution here
        return "";
    }
    public TreeNode deserialize(String data) {
        // Write your solution here
        return null;
    }
}
#include <string>
using namespace std;

string serialize(TreeNode* root) {
    // Write your solution here
    return "";
}

TreeNode* deserialize(const string& data) {
    // Write your solution here
    return nullptr;
}
function serialize(root) {
    // Write your solution here
}

function deserialize(data) {
    // Write your solution here
}
0/9
Common Bugs to Avoid
Wrong: Missing 'X' markers for null childrenNot adding null markers during serialization, causing deserialization to fail or produce incorrect trees.Add 'X' for every null child node during preorder traversal serialization.
Wrong: Incorrect traversal order in serializationUsing inorder or postorder traversal instead of preorder, breaking deserialization assumptions.Use preorder traversal: node, left subtree, right subtree.
Wrong: Negative or zero values serialized incorrectlyConverting integers to strings improperly or ignoring sign, causing wrong deserialization.Convert node values to strings with str() and parse with int() preserving sign.
Wrong: Empty tree serialized as empty string instead of 'X'Not handling null root case explicitly in serialization.Return 'X' when root is null to represent empty tree.
Wrong: TLE on large inputsUsing exponential or quadratic time algorithms instead of linear traversal.Implement O(n) preorder traversal with null markers for serialization and deserialization.
Test Cases
t1_01basic
Input{"root":[1,2,3,null,null,4,5]}
Expected"1,2,X,X,3,4,X,X,5,X,X"

Preorder serialization with 'X' as null marker for the tree [1,2,3,null,null,4,5]. Deserializing reconstructs the exact same tree.

t1_02basic
Input{"root":[10,5,15,null,7,null,20]}
Expected"10,5,X,7,X,X,15,X,20,X,X"

Preorder serialization of tree with mixed null children: root=10, left=5 with right child 7, right=15 with right child 20.

t2_01edge
Input{"root":null}
Expected"X"

Empty tree serializes to 'X' and deserializes back to null.

t2_02edge
Input{"root":[42]}
Expected"42,X,X"

Single node tree serializes to value plus two null markers for children.

t2_03edge
Input{"root":[1,2,null,3]}
Expected"1,2,3,X,X,X,X"

Tree with only left children: 1->2->3, right children are null and must be marked.

t3_01corner
Input{"root":[1,null,2,null,3]}
Expected"1,X,2,X,3,X,X"

Right skewed tree: 1->2->3 with null left children must be serialized correctly.

t3_02corner
Input{"root":[0,-1,1]}
Expected"0,-1,X,X,1,X,X"

Tree with zero and negative values must serialize and deserialize preserving sign and value.

t3_03corner
Input{"root":[1,2,3,4,5,6,7]}
Expected"1,2,4,X,X,5,X,X,3,6,X,X,7,X,X"

Full binary tree to test correct preorder traversal and null markers for leaves.

t4_01performance
Input{"root":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]}
⏱ Performance - must finish in 2000ms

Performance test with n=100 nodes in a balanced tree structure. Serialization and deserialization must run in O(n) time within 2 seconds.

Practice

(1/5)
1. Consider this modified code snippet for the Binary Tree Cameras problem. Which line contains the subtle bug that causes incorrect camera placement?
medium
A. Line returning COVERED_NO_CAM after placing a camera instead of HAS_CAM
B. Line returning NOT_COVERED at the end of dfs
C. Line checking if dfs(root) == NOT_COVERED after traversal
D. Line returning COVERED_NO_CAM when node is null

Solution

  1. Step 1: Identify camera placement logic

    When a child is NOT_COVERED, a camera must be placed at current node and dfs must return HAS_CAM to indicate camera presence.
  2. Step 2: Locate incorrect return

    The code returns COVERED_NO_CAM after placing a camera, which falsely signals no camera here, causing parents to misinterpret coverage.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Returning HAS_CAM is essential after placing a camera [OK]
Hint: Return HAS_CAM after placing camera to signal coverage [OK]
Common Mistakes:
  • Returning COVERED_NO_CAM instead of HAS_CAM after camera placement
  • Forgetting to add camera if root uncovered
  • Mixing coverage states in conditions
2. What is the space complexity of the optimal recursive approach to invert a binary tree, assuming the tree has n nodes and height h?
medium
A. O(n) because each node's children pointers are swapped individually
B. O(n) due to storing all nodes in a queue during traversal
C. O(1) because inversion is done in-place without extra data structures
D. O(h) due to recursion stack depth proportional to tree height

Solution

  1. Step 1: Identify auxiliary space usage in recursion

    The algorithm uses recursion, so the call stack depth is proportional to the height h of the tree.
  2. Step 2: Distinguish between in-place operations and recursion stack

    Swapping pointers is in-place (O(1) per node), but recursion stack space is O(h), not O(n).
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Recursion stack dominates space, proportional to tree height h [OK]
Hint: Recursion stack space depends on tree height, not node count [OK]
Common Mistakes:
  • Confusing in-place operation with O(1) total space
  • Assuming queue-based BFS space applies to recursive DFS
  • Counting node swaps as extra space
3. Suppose the problem is modified so that cameras can monitor nodes up to distance 2 away (i.e., parent, self, children, and grandchildren). Which change to the algorithm is necessary to maintain minimal camera placement?
hard
A. Use the same three-state postorder traversal but add a fourth state to track grandchildren coverage
B. Place cameras greedily on all nodes with uncovered children as before, no change needed
C. Modify DFS to track coverage states up to distance 2 and place cameras accordingly, increasing state complexity
D. Switch to brute force recursion since greedy no longer works

Solution

  1. Step 1: Understand coverage radius increase

    Cameras now cover nodes up to distance 2, so coverage states must reflect grandchildren coverage, not just immediate children.
  2. Step 2: Adjust algorithm states

    The original three states are insufficient; DFS must track coverage at a larger radius, increasing complexity and requiring more nuanced state management.
  3. Step 3: Evaluate options

    Simply adding a fourth state (A) is vague and insufficient without redesign. Greedy placement without state changes (B) fails minimality. Brute force (D) is inefficient and unnecessary.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Expanded coverage radius requires more complex state tracking [OK]
Hint: Larger coverage radius -> more complex states in DFS [OK]
Common Mistakes:
  • Assuming original states suffice for extended coverage
  • Relying on naive greedy placement
  • Switching to brute force unnecessarily
4. Suppose you want to perform a preorder traversal on a binary tree where nodes can have parent pointers but no left or right pointers. Which approach correctly adapts preorder traversal to this scenario without extra space?
hard
A. Use a modified iterative approach that tracks previously visited nodes to avoid revisiting
B. Use recursion on parent pointers to simulate traversal
C. Iteratively traverse using parent pointers and a stack to track visited nodes
D. Use Morris traversal by creating temporary threaded links on parent pointers

Solution

  1. Step 1: Understand traversal constraints

    Without left/right pointers, standard Morris or recursion is not applicable; parent pointers only allow upward traversal.
  2. Step 2: Identify correct approach

    Tracking previously visited nodes iteratively allows preorder traversal by moving up/down without extra space for recursion stack.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Modified iterative approach handles parent-only trees without extra space [OK]
Hint: Parent-only trees require tracking visited nodes iteratively [OK]
Common Mistakes:
  • Trying to use recursion without child pointers
  • Attempting Morris traversal on parent pointers
  • Using stack without tracking visited nodes
5. Suppose the binary tree nodes can have negative values and you want to find the diameter defined as the longest path in terms of number of edges (ignoring node values). Which modification to the standard diameter algorithm is necessary to handle this variant correctly?
hard
A. No modification needed; the standard diameter algorithm based on heights works regardless of node values.
B. Modify the algorithm to sum node values along paths and find maximum sum path instead of longest path.
C. Change the height calculation to ignore negative nodes by treating them as null nodes.
D. Use breadth-first search to find the longest path instead of depth-first traversal.

Solution

  1. Step 1: Recognize diameter depends on path length (edges), not node values.

    Negative values do not affect height or path length calculations.
  2. Step 2: Confirm standard height-based diameter algorithm works unchanged.

    Heights and diameter calculations rely on structure, so no changes are needed.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Diameter depends on edges, not node values [OK]
Hint: Diameter is structural, unaffected by node values [OK]
Common Mistakes:
  • Confusing diameter with max sum path
  • Ignoring negative nodes incorrectly
  • Switching to BFS unnecessarily