Serialize and Deserialize Binary Tree

Given a binary tree, serialize is a process of converting it into sequence of bits (array) and deserialize is the process of converting it back to the original form using that bits.

Example

Input:
          15
         /  \
	/    \
       10     20
              / \
             /   \
            16   25

Output:
Serialzied: 
[15, 10, null, null, 20, 16, null, null, 25, null, null] 
              or 
[15, 10, 20, null, null, 16, 25]

Deserialized:
          15
         /  \
	/    \
       10     20
              / \
             /   \
            16   25

Serialize and Deserialize Binary Tree

We are going to see the both iterative as well as recursive solution for serialize and deserialize the binary tree.

Serialize Binary Tree using DFS and BFS.

The idea is we are going to perform the level-order traversal on the binary tree which is a breadth first search for iterative solution and pre-order traversal which is depth first search for recursive and the store each value of the nodes in the array.

Iterative

  • Use a queue to perform the BFS using level-order traversal on the binary tree.
  • Add the first element in the queue initially and then start iterating the queue.
  • In each iteration, get the first node from the queue add its value in the array and then push its left and right subtree back to the queue.
const serializeIterative = (root) => {
    const res = [];
    //create a queue
    const queue = root ? [root] : [];
    
    //Iterate the queue
    while (queue.length) {
        //Get the top node
        let node = queue.shift();
        
        //If node exists
        if (node) {
            //Push the node value
            res.push(node.val);
            
            //Add the left and right child
            queue.push(node.left || null);
            queue.push(node.right || null);
        } else {
            //Else push null
            res.push(null);
        }
    }
    
    //Remove the trailing nulls to optimize it further
    while (res[res.length - 1] === null){ 
      res.pop(); 
    }
  
    return res;
};
Input:
function Node(val) {
  this.val = val;
  this.left = null;
  this.right = null;
}

const tree = new Node(1);  
tree.left = new Node(2);  
tree.right = new Node(3);   
tree.right.left = new Node(4);  
tree.right.right = new Node(5);

console.log(serializeIterative(tree));

Output:
[1, 2, 3, null, null, 4, 5]

Recursive

For recursive solution we will use the pre-order traversal and perform the DFS and store the values in an array. The traversal will be done by an inner function.

const serializeRecursive = (root) => {
    // array
    let res = [];

    // recursive function to perform pre order traversal on Binary Tree
    function traversal(node) {
        // check for the first null value
        if(node === null) {
            res.push(null);
            return;
        }

        res.push(node.val);
        traversal(node.left);
        traversal(node.right)
    }

    // start the Traversal
    traversal(root);
  
    return res; 
};
Input:
function Node(val) {
  this.val = val;
  this.left = null;
  this.right = null;
}

const tree = new Node(1);  
tree.left = new Node(2);  
tree.right = new Node(3);   
tree.right.left = new Node(4);  
tree.right.right = new Node(5);

console.log(serializeIterative(tree));

Output:
[1, 2, null, null, 3, 4, null, null, 5, null, null]

Time complexity for iterative and recursive solution is O(n) and space complexity is also O(n).


Deserialize Binary Search Tree.

Iterative

  • Use a queue to form the tree again.
  • Create the root node from the first element of the array and push it in the queue and start iterating the queue.
  • In each iteration get the top node and assign the left and right child value to it by fetching the top values from the array, then push this left and right child back to the queue for further formation of subtrees.
const deserializeIterative = (arr) => {
    //Base case
    if (!arr.length){ 
      return null;
    }
    
    //Create the root from first value
    const root = new Node(arr.shift());
    
    //Create the queue
    const queue = [root];
  
    //Iterate the queue
    while (queue.length) {
        let node = queue.shift(), val;
        
        //Set the left and right child values
        node.left = (val = arr.shift()) || val === 0 ? new Node(val) : null;
        node.right = (val = arr.shift()) || val === 0 ? new Node(val) : null;
        
        //Add the left and right child to the queue
        if (node.left) queue.push(node.left);
        if (node.right) queue.push(node.right);
    }
  
    //return the tree
    return root;
};
Input:
console.log(deserializeIterative([15, 10, 20, null, null, 16, 25]));

Output:
          15
         /  \
	/    \
       10     20
              / \
             /   \
            16   25

Recursive

We use the same approach for deserializing recursively as we have done in iterative, all we have to do is call the same function for the right and left sub trees and assign its value. In each the function will pull the top value from the array and return it.

If there is no value in array then terminate the function.

const deserializeRecursive = (arr) => {
    // recursive function
    function reverseTraversal() {

        // check for null or empty value
        if(arr.length == 0) {
            return ;
        }

        // Remove the root node or first element
        const val = arr.shift();

        // check for the null and return null
        if(val == null) return null;

        // Create a new Tree Node
        let node = new Node(val);

        // First check for left elements in tree
        node.left = reverseTraversal();

        // Second check for right elements in tree
        node.right = reverseTraversal();

        // Return the node
        return node;
    }

    return reverseTraversal();
};
Input:
console.log(deserializeRecursive([15, 10, null, null, 20, 16, null, null, 25, null, null]));

Output:
          15
         /  \
	/    \
       10     20
              / \
             /   \
            16   25

Time complexity: O(n) for both solution.
Space complexity: O(n).