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 ofA
C
would be the right child ofB
- 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:
- We need to traverse the entire N-ary tree structure to encode it into a binary tree
- DFS allows us to recursively process each node and its children in a systematic way
- 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
- 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.
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:
- Parent-child relationships
- 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 toB
(via right pointer) B
points toC
(via right pointer)C
points toD
(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:
- Base Case: If the root is
None
, returnNone
- Create Binary Node: Create a new
TreeNode
with the same value as the N-ary node - 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:
- Base Case: If data is
None
, returnNone
- Create N-ary Node: Create a new
Node
with the same value and an empty children list - 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, wheren
is the total number of nodes. Each node is visited exactly once. - Space Complexity:
O(h)
for the recursion stack, whereh
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 EvaluatorExample 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:
-
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. -
Output data structure: The encode function creates a new binary tree with
n
nodes (oneTreeNode
for eachNode
in the N-ary tree). The decode function creates a new N-ary tree withn
nodes. This requiresO(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.
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
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!