Serialize and Deserialize a Binary Tree

  • Write a function to serialize a binary tree into a string
  • Write another function to deserialize that string into initial binary tree

The point:

  • Serialization
  • Make sure to we can identify the value of the root
  • DFS preorder (root, left, right)
  • In the string use ‘#’ to represent empty nodes

Deserialization

  • use preorder traversal to reconstruct the tree
  • the first value is root

Complexity :

Op Time Space
Serialize O(n) O(n)
Deserialize O(n) O(n)
  • O(n) in time in both cases because we need to visit n nodes exactly once. The serialize function converts to a string, which also takes time.
  • O(n) in space in both cases because of the size of recursive call stack. Can grow as large as the height of the binary tree (height max. = n). The serialize function uses a list to store the values of the string.

V1

About Rust :

  • Based on V2 (see 189_intro.ipynb) for easy tree building
  • Explicit index in deserialize()

  • YES : tested on the Rust Playground

TODO :

  • Add more notes in About Rust
type Link = Option<Box<TreeNode>>;

struct TreeNode {
    value: i32,
    left: Link,
    right: Link,
}

impl TreeNode {
    fn new(value: i32) -> Self {
        TreeNode {
            value,
            left: None,
            right: None,
        }
    }

    // Add child on the left
    fn left(mut self, node: TreeNode) -> Self {
        self.left = Some(Box::new(node));
        self
    }

    // Add child on the right
    fn right(mut self, node: TreeNode) -> Self {
        self.right = Some(Box::new(node));
        self
    }
}

fn preorder_print(link: &Link) {
    if let Some(node) = link {
        print!("{} ", node.value);      // Process current node
        preorder_print(&node.left);     // Traverse left child
        preorder_print(&node.right);    // Traverse right child
    }
}

// Helper function to perform serialization through preorder traversal.
fn preorder_serialize (node_link : &Link, serialized_list : &mut Vec<String>){
    match node_link {
        None => {
            // Base case: mark missing nodes as '#'.
            serialized_list.push("#".to_string());
            // return
        },
        Some(node) => {
            // Preorder traversal processes the current node first, then the left and right children
            serialized_list.push(node.value.to_string());
            preorder_serialize(&node.left, serialized_list); // no need for &mut serialized_list in recursive calls
            preorder_serialize(&node.right, serialized_list);
        },
    }
}

fn serialize(root_link : &Link) -> String {
    // Perform a preorder traversal to add node values to a list, then convert the list to a string.
    let mut serialized_list : Vec<String> = Vec::new(); // list of strings (node's values as string)
    preorder_serialize(root_link, &mut serialized_list);
    serialized_list.join(",")
}

fn build_tree(values: &[&str], index: &mut usize) -> Link {
    if *index >= values.len() {
        return None;
    }

    let val = values[*index];
    *index += 1;

    // Base case: '#' indicates a missing node
    if val == "#" {
        return None;
    }

    // Use preorder traversal processes the current node first, then the left and right children
    let parsed_val = val.parse().expect("Invalid integer");
    let mut node = TreeNode::new(parsed_val);
    node.left = build_tree(values, index);
    node.right = build_tree(values, index);
    Some(Box::new(node))
}

fn deserialize(data: &str) -> Link {
    // Obtain the node values by splitting the string using the comma delimiter
    let values: Vec<&str> = data.split(',').map(str::trim).collect();
    let mut index = 0;
    build_tree(&values, &mut index)
}


fn main() { // no main() if this code runs in a Jupyter cell 
    // Build the tree:
    //        5
    //      /   \
    //     9     3
    //    /     / \
    //   2     4   7
    //  /       \
    // 11        6
    let tree = TreeNode::new(5)
        .left(
            TreeNode::new(9)
                .left(
                    TreeNode::new(2)
                        .left(
                            TreeNode::new(11)
                        )
                )
                
        )
        .right(
            TreeNode::new(3)
                .left(
                    TreeNode::new(4)
                        .right(
                            TreeNode::new(6)
                        )
                )
                .right(
                    TreeNode::new(7)
                )
        );

    let root_link = Some(Box::new(tree));
    preorder_print(&root_link); // 5 9 2 11 3 4 6 7   
    println!();
    
    let my_serial = serialize(&root_link);
    println!("{:?}", my_serial); // "5,9,2,11,#,#,#,#,3,4,#,6,#,#,7,#,#"
    
    let root_link2 = deserialize (&my_serial);
    preorder_print(&root_link2); // 5 9 2 11 3 4 6 7     

} // end of local scope OR end of main()

V2 - With iterator

About Rust :

  • Based on V2 (see 189_intro.ipynb) for easy tree building
  • YES : tested on the Rust Playground

TODO :

  • Add comments in code
  • Add more notes in About Rust
type Link = Option<Box<TreeNode>>;

struct TreeNode {
    value: i32,
    left: Link,
    right: Link,
}

impl TreeNode {
    fn new(value: i32) -> Self {
        TreeNode {
            value,
            left: None,
            right: None,
        }
    }

    // Add child on the left
    fn left(mut self, node: TreeNode) -> Self {
        self.left = Some(Box::new(node));
        self
    }

    // Add child on the right
    fn right(mut self, node: TreeNode) -> Self {
        self.right = Some(Box::new(node));
        self
    }
}

fn preorder_print(link: &Link) {
    if let Some(node) = link {
        print!("{} ", node.value);      // Process current node
        preorder_print(&node.left);     // Traverse left child
        preorder_print(&node.right);    // Traverse right child
    }
}

// Helper function to perform serialization through preorder traversal.
fn preorder_serialize (node_link : &Link, serialized_list : &mut Vec<String>){
    match node_link {
        None => {
            serialized_list.push("#".to_string());
            return
        },
        Some(node) => {
            // Preorder traversal processes the current node first, then the left and right children
            serialized_list.push(node.value.to_string());
            preorder_serialize(&node.left, serialized_list); // no need for &mut serialized_list in recursive calls
            preorder_serialize(&node.right, serialized_list);
        },
    }
}

fn serialize(root_link : &Link) -> String{
    // Perform a preorder traversal to add node values to a list, then convert the list to a string.
    let mut serialized_list : Vec<String> = Vec::new(); // list of strings (node's values as string)
    preorder_serialize(&root_link, &mut serialized_list);
    serialized_list.join(",")
}

fn build_tree<'a, I>(values: &mut I) -> Link
where
    I: Iterator<Item = &'a str>,
{
    let val = values.next()?;
    // Base case: '#' indicates a missing node
    if val == "#" {
        return None;
    }
    let val = val.parse().expect("Invalid integer");
    let mut node = TreeNode::new(val);
    node.left = build_tree(values);
    node.right = build_tree(values);
    Some(Box::new(node))
}

fn deserialize(data: &str) -> Link {
    // Obtain the node values by splitting the string using the comma delimiter
    let mut values_iter = data.split(',').map(str::trim);
    build_tree(&mut values_iter)
}


fn main() { // no main() if this code runs in a Jupyter cell 
    // Build the tree:
    //        5
    //      /   \
    //     9     3
    //    /     / \
    //   2     4   7
    //  /       \
    // 11        6
    let tree = TreeNode::new(5)
        .left(
            TreeNode::new(9)
                .left(
                    TreeNode::new(2)
                        .left(
                            TreeNode::new(11)
                        )
                )
                
        )
        .right(
            TreeNode::new(3)
                .left(
                    TreeNode::new(4)
                        .right(
                            TreeNode::new(6)
                        )
                )
                .right(
                    TreeNode::new(7)
                )
        );

    let root_link = Some(Box::new(tree));
    preorder_print(&root_link); // 5 9 2 11 3 4 6 7   
    println!();
    
    let my_serial = serialize(&root_link);
    println!("{:?}", my_serial); // "5,9,2,11,#,#,#,#,3,4,#,6,#,#,7,#,#"
    
    let root_link2 = deserialize (&my_serial);
    preorder_print(&root_link2); // 5 9 2 11 3 4 6 7     

} // end of local scope OR end of main()

Back to top

Published on: Jun 22 2025 at 09:00 AM | Last updated: Jun 22 2025 at 09:00 AM

Copyright © 1964-2025 - 40tude