Serializing and Deserializing Binary Tree

Prerequisite: DFS on Tree

Given a binary tree, write a serialize function that converts the tree into a string and a deserialize function that converts a string back to a binary tree. You may serialize the tree into any string representation you want, as long as it can be deserialized properly.

Try it yourself

Explanation

The serialize function takes the root of a binary tree and turns it into a string representation. The nodes are traversed in depth first search (DFS) order (visiting a node, then its left child, then its right child). To serialize the root of the current binary tree, we'll first append the value at each node to the string, with 'x' appended for null nodes ,then add the serialization for the subtrees at the left and right children (in that order since this is a DFS) by using the serialize function. The 'x' is used to allow us to differentiate leaf nodes from non-leaf nodes when deserializing. This final string is then returned by the serialize function.

To deserialize, we take a string representation of a binary tree and rebuild the original tree. We can accomplish this by splitting the string into tokens and reading them in the original DFS order. If it encounters a node value, it instantiates a new node with the same value. If it encounters an 'x', it interprets this as a leaf node (null).

Serialize

Deserialize

1class Node:
2    def __init__(self, val, left=None, right=None):
3        self.val = val
4        self.left = left
5        self.right = right
6
7def serialize(root):
8    res = []
9    def dfs(root):
10        if not root:
11            res.append('x')
12            return
13        res.append(root.val)
14        dfs(root.left)
15        dfs(root.right)
16    dfs(root)
17    return ' '.join(res)
18
19def deserialize(s):
20    def dfs(nodes):
21        val = next(nodes)
22        if val == 'x': return
23        cur = Node(int(val))
24        cur.left = dfs(nodes)
25        cur.right = dfs(nodes)
26        return cur
27    # create an iterator that returns a token each time we call `next`
28    return dfs(iter(s.split()))
29
30if __name__ == '__main__':
31    def build_tree(nodes):
32        val = next(nodes)
33        if not val or val == 'x': return
34        cur = Node(val)
35        cur.left = build_tree(nodes)
36        cur.right = build_tree(nodes)
37        return cur
38    def print_tree(root):
39        if not root:
40            yield "x"
41            return
42        yield str(root.val)
43        yield from print_tree(root.left)
44        yield from print_tree(root.right)
45    root = build_tree(iter(input().split()))
46    new_root = deserialize(serialize(root))
47    print(' '.join(print_tree(new_root)))
48
1import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.Iterator;
4import java.util.List;
5import java.util.Scanner;
6import java.util.StringJoiner;
7
8class Solution {
9    public static String serialize(Node root) {
10        StringJoiner res = new StringJoiner(" ");
11        serializeDFS(root, res);
12        return res.toString();
13    }
14
15    private static void serializeDFS(Node root, StringJoiner result) {
16        if (root == null) {
17            result.add("x");
18            return;
19        }
20        result.add(Integer.toString(root.val));
21        serializeDFS(root.left, result);
22        serializeDFS(root.right, result);
23    }
24
25    public static Node deserialize(String root) {
26        // create an iterator that returns a token each time we call `next`
27        return deserializeDFS(Arrays.stream(root.split(" ")).iterator());
28    }
29
30    private static Node deserializeDFS(Iterator<String> nodes) {
31        String val = nodes.next();
32        if (val.equals("x")) return null;
33        Node cur = new Node(Integer.parseInt(val));
34        cur.left = deserializeDFS(nodes);
35        cur.right = deserializeDFS(nodes);
36        return cur;
37    }
38
39    /** Driver class, do not change **/
40    static class Node {
41        int val;
42        Node left, right;
43
44        public Node(int val) {
45            this.val = val;
46        }
47
48        public static Node buildTree(Iterator<String> iter) {
49            String nxt = iter.next();
50            if (nxt.equals("x")) return null;
51            Node node = new Node(Integer.parseInt(nxt));
52            node.left = buildTree(iter);
53            node.right = buildTree(iter);
54            return node;
55        }
56
57        public static void printTree(Node root, List<String> out) {
58            if (root == null) {
59                out.add("x");
60            } else {
61                out.add(String.valueOf(root.val));
62                printTree(root.left, out);
63                printTree(root.right, out);
64            }
65        }
66    }
67
68    public static void main(String[] args) {
69        Scanner scanner = new Scanner(System.in);
70        Node root = Node.buildTree(Arrays.stream(scanner.nextLine().split(" ")).iterator());
71        scanner.close();
72        Node newRoot = deserialize(serialize(root));
73        ArrayList<String> out = new ArrayList<>();
74        Node.printTree(newRoot, out);
75        System.out.println(String.join(" ", out));
76    }
77}
78
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5class Solution
6{
7    public class Node<T> {
8        public T val;
9        public Node<T> left;
10        public Node<T> right;
11
12        public Node(T val) {
13            this.val = val;
14        }
15
16        public Node(T val, Node<T> left, Node<T> right) {
17            this.val = val;
18            this.left = left;
19            this.right = right;
20        }
21    }
22
23    public static string Serialize(Node<int> root) {
24        List<string> res = new List<string>();
25        SerializeDFS(root, res);
26        return string.Join(" ", res);
27    }
28
29    private static void SerializeDFS(Node<int> root, List<string> result) {
30        if (root == null) {
31            result.Add("x");
32            return;
33        }
34        result.Add(root.val.ToString());
35        SerializeDFS(root.left, result);
36        SerializeDFS(root.right, result);
37    }
38
39    public static Node<int> Deserialize(string root) {
40        int pos = 0;
41        return DeserializeDFS(root.Split(" ").ToList(), ref pos);
42    }
43
44    private static Node<int> DeserializeDFS(List<string> nodes, ref int pos) {
45        string val = nodes[pos];
46        pos++;
47        if (val == "x") return null;
48        Node<int> cur = new Node<int>(int.Parse(val));
49        cur.left = DeserializeDFS(nodes, ref pos);
50        cur.right = DeserializeDFS(nodes, ref pos);
51        return cur;
52    }
53
54    public static Node<T> BuildTree<T>(List<string> strs, ref int pos, Func<string, T> f) {
55        string val = strs[pos];
56        pos++;
57        if (val == "x") return null;
58        Node<T> left = BuildTree<T>(strs, ref pos, f);
59        Node<T> right = BuildTree<T>(strs, ref pos, f);
60        return new Node<T>(f(val), left, right);
61    }
62
63    public static void PrintTree<T>(Node<T> root, List<string> tree) {
64        if (root == null) {
65            tree.Add("x");
66        } else {
67            tree.Add(root.val.ToString());
68            PrintTree(root.left, tree);
69            PrintTree(root.right, tree);
70        }
71    }
72
73    public static List<string> SplitWords(string s) {
74        return string.IsNullOrEmpty(s) ? new List<string>() : s.Trim().Split(' ').ToList();
75    }
76
77    public static void Main()
78    {
79        List<string> strs = SplitWords(Console.ReadLine());
80        int pos = 0;
81        Node<int> root = BuildTree(strs, ref pos, int.Parse);
82        Node<int> newRoot = Deserialize(Serialize(root));
83        List<string> tree = new List<string>();
84        PrintTree(newRoot, tree);
85        Console.WriteLine(string.Join(" ", tree));
86    }
87}
88
1class Node {
2    constructor(val, left = null, right = null) {
3        this.val = val;
4        this.left = left;
5        this.right = right;
6    }
7}
8
9function serialize(root) {
10    let res = [];
11    serialize_dfs(root, res);
12    return res.join(" ");
13}
14
15function serialize_dfs(root, res) {
16    if (!root) {
17        res.push("x");
18        return;
19    }
20    res.push(root.val);
21    serialize_dfs(root.left, res);
22    serialize_dfs(root.right, res);
23}
24
25function deserialize(s) {
26    // create an iterator that returns a token each time we call `next`
27    return deserialize_dfs(s.split(" ")[Symbol.iterator]());
28}
29
30function deserialize_dfs(nodes) {
31    let val = nodes.next().value;
32    if (val === 'x') return;
33    const cur = new Node(parseInt(val, 10));
34    cur.left = deserialize_dfs(nodes);
35    cur.right = deserialize_dfs(nodes);
36    return cur;
37}
38
39function buildTree(nodes, f) {
40    const val = nodes.next().value;
41    if (val === 'x') return null;
42    const left = buildTree(nodes, f);
43    const right = buildTree(nodes, f);
44    return new Node(f(val), left, right);
45}
46
47function* print_tree(root) {
48    if (!root) {
49        yield "x";
50    } else {
51        yield root.val;
52        yield* print_tree(root.left);
53        yield* print_tree(root.right);
54    }
55}
56
57function splitWords(s) {
58    return s == "" ? [] : s.split(' ');
59}
60
61function* main() {
62    const root = buildTree(splitWords(yield)[Symbol.iterator](), parseInt);
63    const new_root = deserialize(serialize(root));
64    console.log(Array.from(print_tree(new_root)).join(' '));
65}
66
67class EOFError extends Error {}
68{
69    const gen = main();
70    const next = (line) => gen.next(line).done && process.exit();
71    let buf = '';
72    next();
73    process.stdin.setEncoding('utf8');
74    process.stdin.on('data', (data) => {
75        const lines = (buf + data).split('\n');
76        buf = lines.pop();
77        lines.forEach(next);
78    });
79    process.stdin.on('end', () => {
80        buf && next(buf);
81        gen.throw(new EOFError());
82    });
83}
84
1#include <algorithm> // copy
2#include <iostream> // cin, cout
3#include <iterator> // back_inserter, istream_iterator, ostream_iterator, prev
4#include <sstream> // istringstream
5#include <string> // getline, stoi, string, to_string
6#include <vector> // vector
7
8template <typename T>
9struct Node {
10    T val;
11    Node<T>* left;
12    Node<T>* right;
13
14    explicit Node(T val, Node<T>* left = nullptr, Node<T>* right = nullptr)
15        : val{val}, left{left}, right{right} {}
16
17    ~Node() {
18        delete left;
19        delete right;
20    }
21};
22
23void serialize_dfs(Node<int>* root, std::vector<std::string>& res) {
24    if (root == nullptr) {
25        res.emplace_back("x");
26        return;
27    }
28    res.emplace_back(std::to_string(root->val));
29    serialize_dfs(root->left, res);
30    serialize_dfs(root->right, res);
31}
32
33std::string serialize(Node<int>* root) {
34    std::vector<std::string> res;
35    serialize_dfs(root, res);
36    std::stringstream ss;
37    std::copy(res.begin(), res.end(), std::ostream_iterator<std::string>(ss, " "));
38    return ss.str();
39}
40
41Node<int>* deserialize_dfs(std::vector<std::string>::iterator& nodes) {
42    std::string val = *nodes;
43    ++nodes;
44    if (val == "x") return nullptr;
45    Node<int>* cur = new Node<int>(std::stoi(val));
46    cur->left = deserialize_dfs(nodes);
47    cur->right = deserialize_dfs(nodes);
48    return cur;
49}
50
51Node<int>* deserialize(std::string root) {
52    std::istringstream ss(root);
53    std::vector<std::string> nodes;
54    std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(nodes));
55    auto nodes_iter = nodes.begin();
56
57    return deserialize_dfs(nodes_iter);
58}
59
60template<typename T, typename Iter, typename F>
61Node<T>* build_tree(Iter& it, F f) {
62    std::string val = *it;
63    ++it;
64    if (val == "x") return nullptr;
65    Node<T>* left = build_tree<T>(it, f);
66    Node<T>* right = build_tree<T>(it, f);
67    return new Node<T>{f(val), left, right};
68}
69
70template<typename T, typename F>
71void format_tree(Node<T>* node, F f, std::vector<std::string>& out) {
72    if (node == nullptr) {
73        out.emplace_back("x");
74        return;
75    }
76    out.emplace_back(f(node->val));
77    format_tree(node->left, f, out);
78    format_tree(node->right, f, out);
79}
80
81template<typename T>
82std::vector<T> get_words() {
83    std::string line;
84    std::getline(std::cin, line);
85    std::istringstream ss{line};
86    std::vector<T> v;
87    std::copy(std::istream_iterator<T>{ss}, std::istream_iterator<T>{}, std::back_inserter(v));
88    return v;
89}
90
91template<typename T>
92void put_words(const std::vector<T>& v) {
93    if (!v.empty()) {
94        std::copy(v.begin(), std::prev(v.end()), std::ostream_iterator<T>{std::cout, " "});
95        std::cout << v.back();
96    }
97    std::cout << '\n';
98}
99
100int main() {
101    std::vector<std::string> tree_vec = get_words<std::string>();
102    auto tree_it = tree_vec.begin();
103    Node<int>* tree = build_tree<int>(tree_it, [](auto s) { return std::stoi(s); });
104    Node<int>* res = deserialize(serialize(tree));
105    std::vector<std::string> res_vec;
106    format_tree(res, [](auto v) { return std::to_string(v); }, res_vec);
107    put_words(res_vec);
108}
109
1package main
2
3import (
4    "bufio"
5    "fmt"
6    "os"
7    "strconv"
8    "strings"
9)
10
11type Node struct {
12    val int
13    left *Node
14    right *Node
15}
16
17func serialize(root *Node) string {
18  var res []string
19  var dfs func(*Node)
20  dfs = func(root *Node) {
21    if root == nil {
22      res = append(res, "x")
23      return
24    }
25    res = append(res, strconv.Itoa(root.val))
26    dfs(root.left)
27    dfs(root.right)
28  }
29  dfs(root)
30  return strings.Join(res, " ")
31}
32
33func deserialize(s string) *Node {
34  nodes := strings.Fields(s)
35  var index int
36  var dfs func() *Node
37  dfs = func() *Node {
38    val := nodes[index]
39    index++
40    if val == "x" {
41      return nil
42    }
43    num, err := strconv.Atoi(val)
44    if err != nil {
45      panic(err)
46    }
47    cur := &Node{val: num}
48    cur.left = dfs()
49    cur.right = dfs()
50    return cur
51  }
52
53  return dfs()
54}
55
56func buildTree(nodes []string, pos *int) *Node {
57    val := nodes[*pos]
58    *pos++
59    if val == "x" {
60        return nil
61    }
62    v, _ := strconv.Atoi(val)
63    left := buildTree(nodes, pos)
64    right := buildTree(nodes, pos)
65    return &Node{v, left, right}
66}
67
68func splitWords(s string) []string {
69    if s == "" {
70        return []string{}
71    }
72    return strings.Split(s, " ")
73}
74
75func formatIntBinaryTree(node *Node, out []string) []string {
76    if node == nil {
77        out = append(out, "x")
78    } else {
79        out = append(out, strconv.Itoa(node.val))
80        out = formatIntBinaryTree(node.left, out)
81        out = formatIntBinaryTree(node.right, out)
82    }
83    return out
84}
85
86func main() {
87    scanner := bufio.NewScanner(os.Stdin)
88    scanner.Scan()
89    bstIdx := 0
90    root := buildTree(splitWords(scanner.Text()), &bstIdx)
91    res := deserialize(serialize(root))
92    out := make([]string, 0)
93    out = formatIntBinaryTree(res, out)
94    fmt.Println(strings.Join(out, " "))
95}
96

Time Complexity: O(n) to traverse n nodes/elements.

Space Complexity:

  • serialize - O(h) stack memory where h is the height of the tree, which is worst case O(n)
  • deserialize - O(h) stack memory (worst case O(n)) and O(n) new nodes, O(n) dominates the space complexity

Got a question?ย Ask the Teaching Assistantย anything you don't understand.

Still not clear? Ask in the Forum, ย Discordย orย Submitย the part you don't understand to our editors.

โ†
โ†‘TA ๐Ÿ‘จโ€๐Ÿซ