Talentd
FleetCode
Make and follow sheets.Sign in

Serialize and Deserialize Binary Tree solution and explanation

HardBinary TreeBreadth-First SearchDepth-First SearchDesign7 min read61.1% acceptanceUpdated 9 Apr 2026
Serialize and Deserialize Binary Tree - Preorder Traversal - Leetcode 297 - PythonNeetCode · 13:48 · 170K viewsWatch on YouTube
9 more videos on this problem
  1. Serialize and Deserialize Binary Tree - Preorder Traversal - Leetcode 297 - PythonNeetCode · 13:48 · 170K views
  2. 花花酱 LeetCode 297. Serialize and Deserialize Binary Tree - 刷题找工作 EP59Hua Hua · 15:01 · 19K views
  3. [Java] Leetcode 297. Serialize and Deserialize Binary Tree [Binary Tree #9]Eric Programming · 13:15 · 3K views
  4. [LeetCode]297. Serialize and Deserialize Binary Tree 中文山景城一姐 · 7:25 · 3K views
  5. LeetCode 297 Serialize and Deserialize Binary Tree Explanation and Solutionhappygirlzt · 7:34 · 3K views
  6. Serialize and Deserialization Binary Tree, BST deserialization Explained. Leetcode 297, 449InterviewDose · 11:02 · 2K views
  7. Serialize and Deserialize a Binary Tree (LeetCode 297) | Interview Essential | QueuesNikhil Lohia · 19:09 · 2K views
  8. Serialize And Deserialize Tree | LeetCode 297 (Hard)Destination FAANG · 10:51 · 2K views
  9. 【小小福讲Leetcode】LeetCode 297 Serialize and Deserialize Binary Tree (DFS 和 BFS 两种方法)中文详解 Python 实现 (新)小小福LeetCode · 34:08 · 1K views
  10. Serialize & Deserialize A Binary Tree - Crafting Recursive Solutions To Interview ProblemsBack To Back SWE · 15:19 · 106K views

The problem

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

Approach overview

Problem Overview: Design two functions: one converts a binary tree into a string (serialize), and the other reconstructs the same tree from that string (deserialize). The structure of the tree must remain identical after reconstruction, including null children.

Approach 1: Depth‑First Search Serialization (Preorder) (Time: O(n), Space: O(n))

This approach performs a preorder traversal: visit the current node, then the left subtree, then the right subtree. During serialization, append node values to a string and insert a special marker such as # for null children. The key insight is that preorder traversal combined with explicit null markers uniquely represents the tree structure. During deserialization, read tokens sequentially and rebuild the tree recursively: create a node for a value and recursively construct its left and right children. Every node and null placeholder is processed exactly once, giving O(n) time and O(n) space for recursion and storage. This method is common when working with Depth-First Search on a Binary Tree.

Approach 2: Breadth‑First Search Serialization (Level Order) (Time: O(n), Space: O(n))

This approach uses level-order traversal with a queue. During serialization, push the root into a queue and process nodes level by level. Append each node's value to the output string, and insert a placeholder such as # when a child is missing. For deserialization, read the values sequentially and reconstruct the tree by attaching left and right children while dequeuing parent nodes. The queue ensures nodes are connected in the same order they appeared in the level traversal. This method mirrors how many tree problems are solved using Breadth-First Search, and it often feels more intuitive because it directly follows the tree’s level structure.

Both strategies rely on encoding null nodes explicitly. Without null markers, different trees could produce identical serialized sequences, making reconstruction impossible.

Recommended for interviews: The preorder DFS approach is the most common expectation. It demonstrates clear understanding of tree traversal, recursion, and structural encoding. The BFS approach also runs in O(n) time and is equally valid, but interviewers usually prefer the recursive preorder method because the serialization and deserialization logic aligns naturally with tree structure.

Approach 1: Using HashMap for Counting Elements

This approach involves using a HashMap (or dictionary in Python) to count the occurrences of each element in the array. This will help in efficiently checking if an element exists and how many times it appears, which is useful for problems requiring frequency analysis.

def count_elements(arr):
    frequency = {}
    for num in arr:
        if num in frequency:
            frequency[num] += 1
        else:
            frequency[num] = 1
    for key, value in frequency.items():
        print(f'Element {key} appears {value} times')

arr = [1, 3, 1, 2, 3, 4, 5, 3]
count_elements(arr)
import java.util.HashMap;

public class ElementCounter {
    public static void countElements(int[] arr) {
        HashMap<Integer, Integer> frequency = new HashMap<>();
        for (int num : arr) {
            frequency.put(num, frequency.getOrDefault(num, 0) + 1);
        }
        for (var entry : frequency.entrySet()) {
            System.out.println("Element " + entry.getKey() + " appears " + entry.getValue() + " times");
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 1, 2, 3, 4, 5, 3};
        countElements(arr);
    }
}
#include <iostream>
#include <unordered_map>
using namespace std;

void countElements(int arr[], int size) {
    unordered_map<int, int> frequency;
    for (int i = 0; i < size; i++) {
        frequency[arr[i]]++;
    }
    for (auto& kvp : frequency) {
        cout << "Element " << kvp.first << " appears " << kvp.second << " times\n";
    }
}

int main() {
    int arr[] = {1, 3, 1, 2, 3, 4, 5, 3};
    int size = sizeof(arr) / sizeof(arr[0]);
    countElements(arr, size);
    return 0;
}
function countElements(arr) {
    let frequency = {};
    arr.forEach(num => {
        frequency[num] = (frequency[num] || 0) + 1;
    });
    for (let key in frequency) {
        console.log(`Element ${key} appears ${frequency[key]} times`);
    }
}

let arr = [1, 3, 1, 2, 3, 4, 5, 3];
countElements(arr);
#include <stdio.h>
#include <stdlib.h>

void countElements(int arr[], int size) {
    int* hashTable = (int*)calloc(1000, sizeof(int));  // Assuming elements range from 0 to 999
    for (int i = 0; i < size; i++) {
        hashTable[arr[i]]++;
    }
    for (int i = 0; i < 1000; i++) {
        if (hashTable[i] > 0) {
            printf("Element %d appears %d times\n", i, hashTable[i]);
        }
    }
    free(hashTable);
}

int main() {
    int arr[] = {1, 3, 1, 2, 3, 4, 5, 3};
    int size = sizeof(arr) / sizeof(arr[0]);
    countElements(arr, size);
    return 0;
}
using System;
using System.Collections.Generic;

class ElementCounter {
    static void CountElements(int[] arr) {
        Dictionary<int, int> frequency = new Dictionary<int, int>();
        foreach (int num in arr) {
            if (frequency.ContainsKey(num)) {
                frequency[num]++;
            } else {
                frequency[num] = 1;
            }
        }
        foreach (var kvp in frequency) {
            Console.WriteLine($"Element {kvp.Key} appears {kvp.Value} times");
        }
    }

    static void Main() {
        int[] arr = {1, 3, 1, 2, 3, 4, 5, 3};
        CountElements(arr);
    }
}

This Python function uses a dictionary to count occurrences of each number. It iterates over the array, updating counts directly in the dictionary. Python's dict provides average O(1) time complexity for insertion and lookup operations.

Complexity

Time Complexity: O(n), as it traverses the entire list. Space Complexity: O(n), due to storage for the dictionary of unique elements.

Try this approach in the editor

Approach 2: Using Sorting to Identify Repeated Elements

Another approach is sorting the array and then identifying repeated elements by comparison with neighboring elements. This leverages the efficiency of modern sorting algorithms to bring repeated elements together, making it trivial to count consecutive duplicates.

def count_sorted_elements(arr):
    arr.sort()
    count = 1
    for i in range(1, len(arr)):
        if arr[i] == arr[i - 1]:
            count += 1
        else:
            print(f'Element {arr[i - 1]} appears {count} times')
            count = 1
    print(f'Element {arr[-1]} appears {count} times')

arr = [1, 3, 1, 2, 3, 4, 5, 3]
count_sorted_elements(arr)
import java.util.Arrays;

public class ElementSorter {
    public static void countSortedElements(int[] arr) {
        Arrays.sort(arr);
        int count = 1;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] == arr[i - 1]) {
                count++;
            } else {
                System.out.println("Element " + arr[i - 1] + " appears " + count + " times");
                count = 1;
            }
        }
        System.out.println("Element " + arr[arr.length - 1] + " appears " + count + " times");
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 1, 2, 3, 4, 5, 3};
        countSortedElements(arr);
    }
}
#include <iostream>
#include <algorithm>
using namespace std;

void countSortedElements(int arr[], int size) {
    sort(arr, arr + size);
    int count = 1;
    for (int i = 1; i < size; i++) {
        if (arr[i] == arr[i - 1]) {
            count++;
        } else {
            cout << "Element " << arr[i - 1] << " appears " << count << " times\n";
            count = 1;
        }
    }
    cout << "Element " << arr[size - 1] << " appears " << count << " times\n";
}

int main() {
    int arr[] = {1, 3, 1, 2, 3, 4, 5, 3};
    int size = sizeof(arr) / sizeof(arr[0]);
    countSortedElements(arr, size);
    return 0;
}
function countSortedElements(arr) {
    arr.sort((a, b) => a - b);
    let count = 1;
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] === arr[i - 1]) {
            count++;
        } else {
            console.log(`Element ${arr[i - 1]} appears ${count} times`);
            count = 1;
        }
    }
    console.log(`Element ${arr[arr.length - 1]} appears ${count} times`);
}

let arr = [1, 3, 1, 2, 3, 4, 5, 3];
countSortedElements(arr);
#include <stdio.h>
#include <stdlib.h>

int compare(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

void countSortedElements(int arr[], int size) {
    qsort(arr, size, sizeof(int), compare);
    int count = 1;
    for (int i = 1; i < size; i++) {
        if (arr[i] == arr[i - 1]) {
            count++;
        } else {
            printf("Element %d appears %d times\n", arr[i - 1], count);
            count = 1;
        }
    }
    printf("Element %d appears %d times\n", arr[size - 1], count);
}

int main() {
    int arr[] = {1, 3, 1, 2, 3, 4, 5, 3};
    int size = sizeof(arr) / sizeof(arr[0]);
    countSortedElements(arr, size);
    return 0;
}
using System;

class ElementSorter {
    static void CountSortedElements(int[] arr) {
        Array.Sort(arr);
        int count = 1;
        for (int i = 1; i < arr.Length; i++) {
            if (arr[i] == arr[i - 1]) {
                count++;
            } else {
                Console.WriteLine($"Element {arr[i - 1]} appears {count} times");
                count = 1;
            }
        }
        Console.WriteLine($"Element {arr[arr.Length - 1]} appears {count} times");
    }

    static void Main() {
        int[] arr = {1, 3, 1, 2, 3, 4, 5, 3};
        CountSortedElements(arr);
    }
}

This Python method sorts the array and iterates through it to count appearances of each number. The sorted array ensures that duplicates are counted consecutively.

Complexity

Time Complexity: O(n log n) for the sort operation. Space Complexity: O(1) if sorting is done in-place.

Try this approach in the editor

Approach 3: Level Order Traversal

We can use level order traversal to serialize the binary tree. Starting from the root node, we add the nodes of the binary tree to the queue in the order from top to bottom, from left to right. Then we dequeue the nodes in the queue one by one. If the node is not null, we add its value to the serialized string; otherwise, we add a special character #. Finally, we return the serialized string.

During deserialization, we split the serialized string by the delimiter to get a string array, and then add the elements in the string array to the queue in order. The elements in the queue are the nodes of the binary tree. We dequeue the elements from the queue one by one. If the element is not #, we convert it to an integer and use it as the value of the node, and then add the node to the queue; otherwise, we set it to null. Finally, we return the root node.

The time complexity is O(n), and the space complexity is O(n). Where n is the number of nodes in the binary tree.

/**
 * 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 ""
	}
	q := []*TreeNode{root}
	ans := []string{}
	for len(q) > 0 {
		node := q[0]
		q = q[1:]
		if node != nil {
			ans = append(ans, strconv.Itoa(node.Val))
			q = append(q, node.Left)
			q = append(q, node.Right)
		} else {
			ans = append(ans, "#")
		}
	}
	return strings.Join(ans, ",")
}

// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {
	if data == "" {
		return nil
	}
	vals := strings.Split(data, ",")
	v, _ := strconv.Atoi(vals[0])
	i := 1
	root := &TreeNode{Val: v}
	q := []*TreeNode{root}
	for len(q) > 0 {
		node := q[0]
		q = q[1:]
		if x, err := strconv.Atoi(vals[i]); err == nil {
			node.Left = &TreeNode{Val: x}
			q = append(q, node.Left)
		}
		i++
		if x, err := strconv.Atoi(vals[i]); err == nil {
			node.Right = &TreeNode{Val: x}
			q = append(q, node.Right)
		}
		i++
	}
	return root
}

/**
 * Your Codec object will be instantiated and called as such:
 * ser := Constructor();
 * deser := Constructor();
 * data := ser.serialize(root);
 * ans := deser.deserialize(data);
 */
Try this approach in the editor

Complexity comparison

ApproachComplexity
Using HashMap for Counting Elements

Time Complexity: O(n), as it traverses the entire list. Space Complexity: O(n), due to storage for the dictionary of unique elements.

Using Sorting to Identify Repeated Elements

Time Complexity: O(n log n) for the sort operation. Space Complexity: O(1) if sorting is done in-place.

Level Order Traversal–

Complexity analysis

ApproachTimeSpaceWhen to Use
DFS Preorder SerializationO(n)O(n)Preferred interview solution; simple recursive reconstruction of tree structure
BFS Level Order SerializationO(n)O(n)Useful when thinking in level traversal or implementing iterative queue-based logic

Frequently asked questions

Is Serialize and Deserialize Binary Tree easy or hard?

Serialize and Deserialize Binary Tree is classified as a Hard problem on LeetCode. The difficulty comes from designing a reversible encoding scheme that preserves the full tree structure, including null children.

Serialize and Deserialize Binary Tree Python/Java solution

Python and Java implementations typically use preorder traversal with a delimiter-separated string and a null marker such as '#'. Deserialization reads tokens sequentially and recursively reconstructs the tree. Both languages achieve O(n) time and O(n) space complexity.

How to solve Serialize and Deserialize Binary Tree in O(n)?

Traverse the tree using preorder DFS and record values in a string while inserting a special marker like # for null nodes. During reconstruction, read the sequence sequentially and recursively build left and right children. Each node and placeholder is processed once, resulting in O(n) time.

What is the best approach for Serialize and Deserialize Binary Tree?

Preorder depth-first traversal with null markers is the most common solution. The tree is serialized by visiting nodes in preorder and writing a placeholder for missing children. During deserialization, the same order allows recursive reconstruction of the exact structure in O(n) time.

Is Serialize and Deserialize Binary Tree asked at Google/Amazon/Meta?

Serialize and Deserialize Binary Tree frequently appears in interviews at companies like Google, Amazon, Meta, and Microsoft. The problem tests tree traversal, recursion design, and the ability to encode and reconstruct data structures.

What data structure is used in Serialize and Deserialize Binary Tree?

The main data structures include binary trees, recursion stacks, queues for BFS solutions, and strings or arrays to store serialized values. DFS solutions rely on recursive calls, while BFS implementations use a queue for level-order traversal.

What is the time complexity of Serialize and Deserialize Binary Tree?

Both serialization and deserialization run in O(n) time where n is the number of nodes in the tree. Each node is processed exactly once, and null placeholders are handled in constant time operations.

Comments

Now write it yourselfClose this page and solve Serialize and Deserialize Binary Tree in the editor, without looking.
Practise Serialize and Deserialize Binary Tree