Facebook Pixel

431. Encode N-ary Tree to Binary Tree 🔒

Problem Description

This problem asks you to design an algorithm that can convert an N-ary tree (where each node can have up to N children) into a binary tree (where each node can have at most 2 children), and then convert it back to the original N-ary tree structure.

The key requirements are:

  • Create an encode function that takes an N-ary tree and transforms it into a binary tree
  • Create a decode function that takes the encoded binary tree and reconstructs the original N-ary tree
  • The encoding/decoding process must be reversible - you should get back the exact same N-ary tree structure after encoding and then decoding

The solution uses a clever mapping strategy:

  • The left child of each binary tree node points to the first child of the corresponding N-ary tree node
  • The right child of each binary tree node points to the next sibling in the N-ary tree

For example, if an N-ary node has children [A, B, C]:

  • The binary tree representation would have A as the left child
  • B would be the right child of A
  • C would be the right child of B
  • This creates a linked-list-like structure of siblings using the right pointers

During decoding, the process is reversed:

  • Start from the left child of the binary node
  • Traverse through all right pointers to collect all siblings
  • These siblings become the children of the reconstructed N-ary node

This approach ensures that any N-ary tree structure can be faithfully represented as a binary tree and perfectly reconstructed back to its original form.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: While this problem involves trees (N-ary and binary), trees are a special type of graph. We're working with nodes and their connections (parent-child relationships).

Is it a tree?

  • Yes: The problem explicitly deals with converting between two types of trees - an N-ary tree and a binary tree. Both are rooted tree structures with hierarchical relationships between nodes.

DFS

  • Yes: We arrive at DFS (Depth-First Search) as our algorithm choice.

Conclusion: The flowchart suggests using a DFS approach for this problem.

This makes perfect sense because:

  1. We need to traverse the entire N-ary tree structure to encode it into a binary tree
  2. DFS allows us to recursively process each node and its children in a systematic way
  3. The encoding strategy (mapping first child to left pointer, siblings to right pointers) naturally fits a recursive DFS pattern where we:
    • Process the current node
    • Recursively encode the first child as the left subtree
    • Recursively encode remaining children as a chain of right nodes
  4. The decoding process also uses DFS to traverse the binary tree and reconstruct the N-ary tree by following the left-right pointer pattern in reverse

The recursive nature of DFS perfectly matches the recursive structure of both encoding and decoding operations in this tree transformation problem.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The challenge here is that binary tree nodes have exactly 2 child pointers (left and right), while N-ary tree nodes can have any number of children. We need a systematic way to map multiple children into just two pointers.

The key insight is to think about what information we need to preserve:

  1. Parent-child relationships
  2. Sibling relationships (which child comes after which)

Since we only have two pointers in a binary tree, we need to be creative. One natural approach is to use:

  • One pointer to represent the "vertical" relationship (parent to children)
  • Another pointer to represent the "horizontal" relationship (between siblings)

This leads us to the elegant solution:

  • Use the left pointer to point to the first child (preserving the parent-child relationship)
  • Use the right pointer to create a linked list of siblings (preserving the order of children)

Think of it like converting a family tree into a different format. If a parent has children [A, B, C, D]:

  • Instead of the parent pointing to all four children directly
  • The parent points only to A (via left pointer)
  • Then A points to B (via right pointer)
  • B points to C (via right pointer)
  • C points to D (via right pointer)

This creates a "chain" of siblings connected through right pointers, while maintaining the parent-child connection through the left pointer.

The beauty of this approach is that it's completely reversible. When decoding:

  • Start from the left child (first child of N-ary node)
  • Follow the chain of right pointers to collect all siblings
  • These become the children array of the reconstructed N-ary node

This encoding scheme ensures no information is lost during the transformation, making perfect reconstruction possible.

Solution Approach

The solution implements the encoding and decoding using recursive DFS traversal. Let's walk through both processes:

Encoding (N-ary to Binary Tree)

The encode function recursively transforms an N-ary tree node into a binary tree node:

  1. Base Case: If the root is None, return None
  2. Create Binary Node: Create a new TreeNode with the same value as the N-ary node
  3. Handle Children:
    • If there are no children, return the node as-is
    • Otherwise, encode the first child and set it as the left child of the binary node
    • For remaining children (from index 1 onwards), chain them as right siblings:
      left = self.encode(root.children[0])
      node.left = left
      for child in root.children[1:]:
          left.right = self.encode(child)
          left = left.right

The key pattern here is creating a linked list of siblings using the right pointers. Each iteration:

  • Encodes the next child
  • Attaches it as the right child of the previous sibling
  • Moves the pointer forward

Decoding (Binary Tree to N-ary)

The decode function reverses the encoding process:

  1. Base Case: If data is None, return None
  2. Create N-ary Node: Create a new Node with the same value and an empty children list
  3. Reconstruct Children:
    • If there's no left child, the N-ary node has no children
    • Otherwise, traverse the chain of right pointers starting from the left child:
      left = data.left
      while left:
          node.children.append(self.decode(left))
          left = left.right

This traversal collects all siblings (connected via right pointers) and adds them to the children array of the N-ary node.

Time and Space Complexity

  • Time Complexity: O(n) for both encoding and decoding, where n is the total number of nodes. Each node is visited exactly once.
  • Space Complexity: O(h) for the recursion stack, where h is the height of the tree. The encoded binary tree uses the same number of nodes as the original N-ary tree.

The elegance of this solution lies in its simplicity - by using the left pointer for the first child and right pointers for siblings, we create a bijective mapping between N-ary and binary trees without losing any structural information.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through encoding and decoding a simple N-ary tree to understand how the algorithm works.

Original N-ary Tree:

       1
    /  |  \
   2   3   4
  / \
 5   6

Step 1: Encoding Process

Starting with node 1:

  • Node 1 has children [2, 3, 4]
  • Create binary node with value 1
  • Set node 2 (first child) as the left child
  • Chain nodes 3 and 4 as right siblings of node 2

For node 2:

  • Node 2 has children [5, 6]
  • Create binary node with value 2
  • Set node 5 (first child) as the left child
  • Chain node 6 as the right sibling of node 5

For nodes 3, 4, 5, 6:

  • These are leaf nodes, so just create binary nodes with their values

Resulting Binary Tree:

       1
      /
     2 → 3 → 4
    /
   5 → 6

(Arrows → represent right pointers)

Step 2: Decoding Process

Starting with binary node 1:

  • Create N-ary node with value 1
  • Node 1 has a left child (node 2), so traverse the right-pointer chain:
    • Add decoded node 2 to children
    • Follow right pointer to node 3, add to children
    • Follow right pointer to node 4, add to children
  • Result: Node 1 with children [2, 3, 4]

For binary node 2:

  • Create N-ary node with value 2
  • Node 2 has a left child (node 5), so traverse the right-pointer chain:
    • Add decoded node 5 to children
    • Follow right pointer to node 6, add to children
  • Result: Node 2 with children [5, 6]

For binary nodes 3, 4, 5, 6:

  • No left children, so create N-ary nodes with empty children arrays

Final Reconstructed N-ary Tree:

       1
    /  |  \
   2   3   4
  / \
 5   6

The tree is perfectly reconstructed! The key insight is that the left pointer preserves the parent-to-first-child relationship, while the right pointers create a linked list of siblings that can be traversed during decoding to reconstruct the full children array.

Solution Implementation

1"""
2# Definition for a Node.
3class Node:
4    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
5        self.val = val
6        self.children = children
7"""
8
9"""
10# Definition for a binary tree node.
11class TreeNode:
12    def __init__(self, x):
13        self.val = x
14        self.left = None
15        self.right = None
16"""
17
18from typing import Optional, List
19
20
21class Codec:
22    def encode(self, root: Optional['Node']) -> Optional[TreeNode]:
23        """
24        Encodes an n-ary tree to a binary tree.
25      
26        Strategy: For each n-ary node, its first child becomes the left child in binary tree,
27        and all subsequent children are chained as right children of each other.
28      
29        Args:
30            root: Root of the n-ary tree
31          
32        Returns:
33            Root of the encoded binary tree
34        """
35        # Base case: empty tree
36        if root is None:
37            return None
38      
39        # Create binary tree node with same value
40        binary_node = TreeNode(root.val)
41      
42        # If no children, return the node as-is
43        if not root.children:
44            return binary_node
45      
46        # First child of n-ary tree becomes left child of binary tree
47        binary_node.left = self.encode(root.children[0])
48      
49        # Chain remaining children as right siblings in the binary tree
50        current = binary_node.left
51        for child in root.children[1:]:
52            current.right = self.encode(child)
53            current = current.right
54          
55        return binary_node
56
57    def decode(self, data: Optional[TreeNode]) -> Optional['Node']:
58        """
59        Decodes a binary tree back to an n-ary tree.
60      
61        Strategy: The left child in binary tree represents the first child,
62        and all right children of that node represent siblings in the n-ary tree.
63      
64        Args:
65            data: Root of the binary tree
66          
67        Returns:
68            Root of the decoded n-ary tree
69        """
70        # Base case: empty tree
71        if data is None:
72            return None
73      
74        # Create n-ary node with same value and empty children list
75        nary_node = Node(data.val, [])
76      
77        # If no left child, this node has no children
78        if data.left is None:
79            return nary_node
80      
81        # Traverse the chain of right siblings to collect all children
82        current = data.left
83        while current:
84            nary_node.children.append(self.decode(current))
85            current = current.right
86          
87        return nary_node
88
89
90# Your Codec object will be instantiated and called as such:
91# codec = Codec()
92# codec.decode(codec.encode(root))
93
1/*
2// Definition for a Node.
3class Node {
4    public int val;
5    public List<Node> children;
6
7    public Node() {}
8
9    public Node(int _val) {
10        val = _val;
11    }
12
13    public Node(int _val, List<Node> _children) {
14        val = _val;
15        children = _children;
16    }
17};
18*/
19
20/**
21 * Definition for a binary tree node.
22 * public class TreeNode {
23 *     int val;
24 *     TreeNode left;
25 *     TreeNode right;
26 *     TreeNode(int x) { val = x; }
27 * }
28 */
29
30/**
31 * Codec class to encode an N-ary tree to a binary tree and decode it back.
32 * 
33 * Encoding strategy:
34 * - The first child of an N-ary node becomes the left child in the binary tree
35 * - All subsequent siblings are linked as right children in a chain
36 * 
37 * Decoding strategy:
38 * - The left child in binary tree represents the first child of N-ary node
39 * - Following the right pointers from the left child gives all siblings
40 */
41class Codec {
42  
43    /**
44     * Encodes an n-ary tree to a binary tree.
45     * 
46     * @param root The root of the N-ary tree to encode
47     * @return The root of the equivalent binary tree
48     */
49    public TreeNode encode(Node root) {
50        // Base case: null input returns null
51        if (root == null) {
52            return null;
53        }
54      
55        // Create binary tree node with same value
56        TreeNode binaryNode = new TreeNode(root.val);
57      
58        // If no children exist, return the node as is
59        if (root.children == null || root.children.isEmpty()) {
60            return binaryNode;
61        }
62      
63        // Encode first child as left child of binary node
64        TreeNode firstChildEncoded = encode(root.children.get(0));
65        binaryNode.left = firstChildEncoded;
66      
67        // Link remaining children as right siblings in a chain
68        TreeNode currentNode = firstChildEncoded;
69        for (int i = 1; i < root.children.size(); i++) {
70            // Encode each subsequent child and link it as right sibling
71            currentNode.right = encode(root.children.get(i));
72            currentNode = currentNode.right;
73        }
74      
75        return binaryNode;
76    }
77
78    /**
79     * Decodes a binary tree back to an n-ary tree.
80     * 
81     * @param data The root of the binary tree to decode
82     * @return The root of the equivalent N-ary tree
83     */
84    public Node decode(TreeNode data) {
85        // Base case: null input returns null
86        if (data == null) {
87            return null;
88        }
89      
90        // Create N-ary node with same value and empty children list
91        Node naryNode = new Node(data.val, new ArrayList<>());
92      
93        // If no left child exists, node has no children
94        if (data.left == null) {
95            return naryNode;
96        }
97      
98        // Traverse the chain of right siblings starting from left child
99        TreeNode currentBinaryNode = data.left;
100        while (currentBinaryNode != null) {
101            // Recursively decode each node and add as child
102            naryNode.children.add(decode(currentBinaryNode));
103            // Move to next sibling (linked via right pointer)
104            currentBinaryNode = currentBinaryNode.right;
105        }
106      
107        return naryNode;
108    }
109}
110
111// Your Codec object will be instantiated and called as such:
112// Codec codec = new Codec();
113// codec.decode(codec.encode(root));
114
1/*
2// Definition for a Node.
3class Node {
4public:
5    int val;
6    vector<Node*> children;
7
8    Node() {}
9
10    Node(int _val) {
11        val = _val;
12    }
13
14    Node(int _val, vector<Node*> _children) {
15        val = _val;
16        children = _children;
17    }
18};
19*/
20
21/**
22 * Definition for a binary tree node.
23 * struct TreeNode {
24 *     int val;
25 *     TreeNode *left;
26 *     TreeNode *right;
27 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
28 * };
29 */
30
31class Codec {
32public:
33    /**
34     * Encodes an n-ary tree to a binary tree.
35     * Strategy: First child becomes left child in binary tree,
36     * siblings are connected through right pointers in a chain.
37     * 
38     * @param root Root of the n-ary tree
39     * @return Root of the encoded binary tree
40     */
41    TreeNode* encode(Node* root) {
42        // Base case: empty tree
43        if (root == nullptr) {
44            return nullptr;
45        }
46      
47        // Create binary tree node with same value
48        TreeNode* binaryNode = new TreeNode(root->val);
49      
50        // If no children, return the node as is
51        if (root->children.empty()) {
52            return binaryNode;
53        }
54      
55        // Encode first child as left child of binary node
56        TreeNode* currentChild = encode(root->children[0]);
57        binaryNode->left = currentChild;
58      
59        // Encode remaining children as right siblings in a chain
60        for (size_t i = 1; i < root->children.size(); ++i) {
61            currentChild->right = encode(root->children[i]);
62            currentChild = currentChild->right;
63        }
64      
65        return binaryNode;
66    }
67
68    /**
69     * Decodes a binary tree back to an n-ary tree.
70     * Strategy: Left child in binary tree becomes first child,
71     * right pointers form the sibling chain.
72     * 
73     * @param root Root of the binary tree
74     * @return Root of the decoded n-ary tree
75     */
76    Node* decode(TreeNode* root) {
77        // Base case: empty tree
78        if (root == nullptr) {
79            return nullptr;
80        }
81      
82        // Create n-ary tree node with same value and empty children vector
83        Node* naryNode = new Node(root->val, vector<Node*>());
84      
85        // If no left child, node has no children
86        if (root->left == nullptr) {
87            return naryNode;
88        }
89      
90        // Traverse the chain of siblings (connected by right pointers)
91        TreeNode* currentChild = root->left;
92        while (currentChild != nullptr) {
93            // Recursively decode each child and add to children vector
94            naryNode->children.push_back(decode(currentChild));
95            currentChild = currentChild->right;
96        }
97      
98        return naryNode;
99    }
100};
101
102// Your Codec object will be instantiated and called as such:
103// Codec codec;
104// codec.decode(codec.encode(root));
105
1/**
2 * Definition for _Node.
3 * class _Node {
4 *     val: number
5 *     children: _Node[]
6 *
7 *     constructor(v: number) {
8 *         this.val = v;
9 *         this.children = [];
10 *     }
11 * }
12 */
13
14/**
15 * Definition for a binary tree node.
16 * class TreeNode {
17 *     val: number
18 *     left: TreeNode | null
19 *     right: TreeNode | null
20 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
21 *         this.val = (val===undefined ? 0 : val)
22 *         this.left = (left===undefined ? null : left)
23 *         this.right = (right===undefined ? null : right)
24 *     }
25 * }
26 */
27
28/**
29 * Encodes an n-ary tree to a binary tree.
30 * Strategy: First child becomes left child, siblings are chained as right children
31 * @param root - The root of the n-ary tree
32 * @returns The root of the encoded binary tree
33 */
34function serialize(root: _Node | null): TreeNode | null {
35    // Base case: empty tree
36    if (root === null) {
37        return null;
38    }
39  
40    // Create binary tree node with same value
41    const binaryNode: TreeNode = new TreeNode(root.val);
42  
43    // If no children, return the node as-is
44    if (root.children.length === 0) {
45        return binaryNode;
46    }
47  
48    // First child becomes the left child in binary tree
49    const firstChildBinary: TreeNode | null = serialize(root.children[0]);
50    binaryNode.left = firstChildBinary;
51  
52    // Chain remaining children as right siblings
53    let currentBinaryNode: TreeNode | null = firstChildBinary;
54    for (let i = 1; i < root.children.length; i++) {
55        if (currentBinaryNode !== null) {
56            // Each sibling becomes the right child of the previous sibling
57            currentBinaryNode.right = serialize(root.children[i]);
58            currentBinaryNode = currentBinaryNode.right;
59        }
60    }
61  
62    return binaryNode;
63}
64
65/**
66 * Decodes a binary tree back to an n-ary tree.
67 * Strategy: Left child and its right siblings become children of the n-ary node
68 * @param root - The root of the binary tree
69 * @returns The root of the decoded n-ary tree
70 */
71function deserialize(root: TreeNode | null): _Node | null {
72    // Base case: empty tree
73    if (root === null) {
74        return null;
75    }
76  
77    // Create n-ary tree node with same value
78    const naryNode: _Node = new _Node(root.val);
79  
80    // If no left child, this node has no children
81    if (root.left === null) {
82        return naryNode;
83    }
84  
85    // Traverse the left child and all its right siblings
86    let currentBinaryNode: TreeNode | null = root.left;
87    while (currentBinaryNode !== null) {
88        // Each node in the chain becomes a child of the n-ary node
89        const childNode: _Node | null = deserialize(currentBinaryNode);
90        if (childNode !== null) {
91            naryNode.children.push(childNode);
92        }
93        // Move to the next sibling (right child in binary representation)
94        currentBinaryNode = currentBinaryNode.right;
95    }
96  
97    return naryNode;
98}
99

Time and Space Complexity

Time Complexity: O(n)

The encode function visits each node in the N-ary tree exactly once. For each node with children, it processes the first child recursively and then iterates through the remaining children, making recursive calls for each. Since every node is visited exactly once during the encoding process, the time complexity is O(n).

Similarly, the decode function visits each node in the binary tree exactly once. It traverses the left child and then follows the chain of right pointers to reconstruct the children list. Each binary tree node (which corresponds to an N-ary tree node) is processed exactly once, resulting in O(n) time complexity.

Space Complexity: O(n)

The space complexity consists of two components:

  1. Recursive call stack: In the worst case, the N-ary tree could be skewed (e.g., each node has only one child), leading to a recursion depth of O(n). The encode function makes recursive calls that can go as deep as the height of the tree, and similarly for decode.

  2. Output data structure: The encode function creates a new binary tree with n nodes (one TreeNode for each Node in the N-ary tree). The decode function creates a new N-ary tree with n nodes. This requires O(n) space for storing the new tree structure.

Therefore, the overall space complexity is O(n), where n is the number of nodes in the N-ary tree.

Common Pitfalls

Pitfall 1: Incorrectly Handling the Sibling Chain During Encoding

A common mistake is trying to set both left and right children directly on the binary node, rather than chaining siblings properly:

Incorrect Implementation:

def encode(self, root):
    if not root:
        return None
  
    binary_node = TreeNode(root.val)
    if root.children:
        # WRONG: This loses all children except the first two!
        binary_node.left = self.encode(root.children[0])
        if len(root.children) > 1:
            binary_node.right = self.encode(root.children[1])
  
    return binary_node

Why it's wrong: This approach only preserves the first two children and discards the rest. The right pointer of the binary node should NOT point to the second child directly.

Correct Approach:

def encode(self, root):
    if not root:
        return None
  
    binary_node = TreeNode(root.val)
    if root.children:
        binary_node.left = self.encode(root.children[0])
      
        # Chain siblings using right pointers
        current = binary_node.left
        for child in root.children[1:]:
            current.right = self.encode(child)
            current = current.right
  
    return binary_node

Pitfall 2: Forgetting to Recursively Decode Child Nodes

Another frequent error is directly appending the binary tree nodes to the children list without recursively decoding them:

Incorrect Implementation:

def decode(self, data):
    if not data:
        return None
  
    nary_node = Node(data.val, [])
  
    current = data.left
    while current:
        # WRONG: Appending TreeNode instead of decoded Node
        nary_node.children.append(current)
        current = current.right
  
    return nary_node

Why it's wrong: This creates an n-ary tree with TreeNode objects as children instead of properly decoded Node objects, mixing up the two tree types.

Correct Approach:

def decode(self, data):
    if not data:
        return None
  
    nary_node = Node(data.val, [])
  
    current = data.left
    while current:
        # Recursively decode each child
        nary_node.children.append(self.decode(current))
        current = current.right
  
    return nary_node

Pitfall 3: Not Preserving Empty Children Lists

Some implementations forget to initialize the children list when creating n-ary nodes, which can cause issues:

Incorrect Implementation:

def decode(self, data):
    if not data:
        return None
  
    # WRONG: Not initializing children list
    nary_node = Node(data.val)
  
    # This might fail if Node expects children to be a list
    if data.left:
        nary_node.children = []  # Only initialized when children exist
        # ... rest of the code

Correct Approach:

def decode(self, data):
    if not data:
        return None
  
    # Always initialize with empty list
    nary_node = Node(data.val, [])
  
    # Now safely append children if they exist
    current = data.left
    while current:
        nary_node.children.append(self.decode(current))
        current = current.right
  
    return nary_node

Key Takeaway

The most critical aspect to remember is the mapping relationship:

  • Left pointer in binary tree = First child in n-ary tree
  • Right pointer in binary tree = Next sibling in n-ary tree

Visualizing this as creating a "linked list of siblings" using right pointers helps avoid confusion about which pointer should point where.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More