Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Tree Traversal - Understanding left/right child relationships and how to build binary trees
  • N-ary Tree Structure - Familiarity with trees where nodes can have multiple children stored in a list
  • BFS (Breadth-First Search) - Using queues to process nodes level by level
  • DFS (Depth-First Search) - Recursive tree traversal for encoding/decoding operations
  • Left-Child Right-Sibling Representation - The key encoding technique where first child becomes left child and siblings chain as right children

1. BFS (Breadth-First Search) Traversal

Intuition

To encode an N-ary tree into a binary tree, we need a consistent rule for representing multiple children. The standard approach is the "left-child right-sibling" representation: the first child of an N-ary node becomes the left child of the binary node, and subsequent siblings are chained as right children. Using BFS, we process nodes level by level, building the binary tree structure while maintaining the parent-child relationships through a queue.

Algorithm

Encode:

  1. If the root is null, return null.
  2. Create a binary tree root with the same value.
  3. Use a queue storing pairs of (binary node, N-ary node).
  4. For each N-ary node's children:
    • Link them as a chain: first child to left, rest as right siblings.
    • Add each child pair to the queue.
  5. Return the binary tree root.

Decode:

  1. If the root is null, return null.
  2. Create an N-ary root with the same value and an empty children list.
  3. Use a queue storing pairs of (N-ary node, binary node).
  4. For each binary node:
    • Traverse the left child's right chain to reconstruct all children.
    • Add each child pair to the queue.
  5. Return the N-ary tree root.
"""
# Definition for a Node.
class Node(object):
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

"""
# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
"""

class Codec:
    def encode(self, root):
        """Encodes an n-ary tree to a binary tree.
        """
        if not root:
            return None

        rootNode = TreeNode(root.val)
        queue = deque([(rootNode, root)])

        while queue:
            parent, curr = queue.popleft()
            prevBNode = None
            headBNode = None
            # traverse each child one by one
            for child in curr.children:
                newBNode = TreeNode(child.val)
                if prevBNode:
                    prevBNode.right = newBNode
                else:
                    headBNode = newBNode
                prevBNode = newBNode
                queue.append((newBNode, child))

            # use the first child in the left node of parent
            parent.left = headBNode

        return rootNode


    def decode(self, data):
        """Decodes your binary tree to an n-ary tree.
        """
        if not data:
            return None

        # should set the default value to [] rather than None,
        # otherwise it wont pass the test cases.
        rootNode = Node(data.val, [])

        queue = deque([(rootNode, data)])

        while queue:
            parent, curr = queue.popleft()

            firstChild = curr.left
            sibling = firstChild

            while sibling:
                # Note: the initial value of the children list should not be None, which is assumed by the online judge.
                newNode = Node(sibling.val, [])
                parent.children.append(newNode)
                queue.append((newNode, sibling))
                sibling = sibling.right

        return rootNode

Time & Space Complexity

  • Time complexity: O(N)O(N)

  • Space complexity:

    • encode() : O(L)O(L).

    • decode() : O(L)O(L).

    • Since LL is proportional to NN in the worst case, we could further generalize the space complexity to O(N)O(N).

Where NN is the number of nodes in the N-ary tree, and LL is the maximum number of nodes that reside at the same level.


2. DFS (Depth-First Search) Traversal

Intuition

DFS offers a more elegant recursive solution using the same left-child right-sibling encoding. For each N-ary node, we recursively encode its first child and attach it as the left child of the binary node. The remaining children are encoded and linked as right siblings of the first child. Decoding reverses this: we traverse the left child's right chain to rebuild the children list.

Algorithm

Encode:

  1. If the root is null, return null.
  2. Create a binary node with the root's value.
  3. Recursively encode the first child and set it as left.
  4. For remaining children, recursively encode each and chain them as right siblings.
  5. Return the binary node.

Decode:

  1. If the root is null, return null.
  2. Create an N-ary node with the root's value and an empty children list.
  3. Start at the binary node's left child.
  4. While the current sibling exists:
    • Recursively decode it and add to the children list.
    • Move to the right sibling.
  5. Return the N-ary node.
"""
# Definition for a Node.
class Node(object):
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

"""
# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
"""

class Codec:

    def encode(self, root):
        """Encodes an n-ary tree to a binary tree.
        """
        if not root:
            return None

        rootNode = TreeNode(root.val)
        if len(root.children) > 0:
            firstChild = root.children[0]
            rootNode.left = self.encode(firstChild)

        # the parent for the rest of the children
        curr = rootNode.left

        # encode the rest of the children
        for i in range(1, len(root.children)):
            curr.right = self.encode(root.children[i])
            curr = curr.right

        return rootNode


    def decode(self, data):
        """Decodes your binary tree to an n-ary tree.
        """
        if not data:
            return None

        rootNode = Node(data.val, [])

        curr = data.left
        while curr:
            rootNode.children.append(self.decode(curr))
            curr = curr.right

        return rootNode

Time & Space Complexity

  • Time complexity: O(N)O(N)

  • Space complexity: O(D)O(D)

    • Since DD is proportional to NN in the worst case, we could further generalize the space complexity to O(N)O(N)

    • Unlike the BFS algorithm, we don't use the queue data structure in the DFS algorithm. However, implicitly the algorithm would consume more space in the function call stack due to the recursive function calls.

    • And this consumption of call stack space is the main space complexity for our DFS algorithm. As we can see, the size of the call stack at any moment is exactly the number of level where the currently visited node resides, e.g. for the root node (level 0), the recursive call stack is empty.

Where NN is the number of nodes in the N-ary tree, and DD is the depth of the N-ary tree.


Common Pitfalls

Confusing Left-Child and Right-Sibling Roles

In the encoding scheme, left points to the first child of the N-ary node, while right links siblings together. A common mistake is reversing these roles or using right for children, which breaks the bidirectional mapping and makes decoding impossible.

Not Initializing the Children List

When decoding, the N-ary node's children list must be initialized as an empty list, not null or None. Many online judges expect an empty list for leaf nodes, and failing to initialize causes null pointer exceptions when appending children.

Forgetting to Handle the Empty Tree Case

Both encode and decode must handle null input and return null. Forgetting this base case causes null pointer exceptions when the root is null, as the algorithm tries to access properties of a non-existent node.

Incorrect Sibling Chain Traversal During Decode

When decoding, you must traverse the entire right-sibling chain starting from the left child. A common mistake is only processing the immediate left child without following the right pointers, which loses all siblings after the first child.