Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

431. Encode N-ary Tree to Binary Tree - Leetcode Solution

Code Implementation

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children if children is not None else []

# Definition for a Binary Tree Node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""

class Codec:
    def encode(self, root):
        if not root:
            return None
        b_root = TreeNode(root.val)
        if root.children:
            b_root.left = self._encode_children(root.children)
        return b_root

    def _encode_children(self, children):
        head = None
        cur = None
        for child in children:
            node = TreeNode(child.val)
            node.left = self._encode_children(child.children)
            if not head:
                head = node
            else:
                cur.right = node
            cur = node
        return head

    def decode(self, data):
        if not data:
            return None
        n_root = Node(data.val, [])
        n_root.children = self._decode_children(data.left)
        return n_root

    def _decode_children(self, node):
        children = []
        while node:
            n_node = Node(node.val, self._decode_children(node.left))
            children.append(n_node)
            node = node.right
        return children
      
/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;
    Node() {}
    Node(int _val) { val = _val; }
    Node(int _val, vector<Node*> _children) { val = _val; children = _children; }
};

// Definition for a Binary Tree Node.
class TreeNode {
public:
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
*/

class Codec {
public:
    TreeNode* encode(Node* root) {
        if (!root) return nullptr;
        TreeNode* bRoot = new TreeNode(root->val);
        if (!root->children.empty()) {
            bRoot->left = encodeChildren(root->children);
        }
        return bRoot;
    }
    
    TreeNode* encodeChildren(const vector<Node*>& children) {
        TreeNode* head = nullptr;
        TreeNode* cur = nullptr;
        for (Node* child : children) {
            TreeNode* node = new TreeNode(child->val);
            node->left = encodeChildren(child->children);
            if (!head) head = node;
            else cur->right = node;
            cur = node;
        }
        return head;
    }
    
    Node* decode(TreeNode* data) {
        if (!data) return nullptr;
        Node* nRoot = new Node(data->val, {});
        nRoot->children = decodeChildren(data->left);
        return nRoot;
    }
    
    vector<Node*> decodeChildren(TreeNode* node) {
        vector<Node*> children;
        while (node) {
            Node* nNode = new Node(node->val, decodeChildren(node->left));
            children.push_back(nNode);
            node = node->right;
        }
        return children;
    }
};
      
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;
    public Node() {}
    public Node(int _val) { val = _val; }
    public Node(int _val, List<Node> _children) { val = _val; children = _children; }
}

// Definition for a Binary Tree Node.
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
*/

class Codec {
    public TreeNode encode(Node root) {
        if (root == null) return null;
        TreeNode bRoot = new TreeNode(root.val);
        if (root.children != null && !root.children.isEmpty()) {
            bRoot.left = encodeChildren(root.children);
        }
        return bRoot;
    }
    
    private TreeNode encodeChildren(List<Node> children) {
        TreeNode head = null, cur = null;
        for (Node child : children) {
            TreeNode node = new TreeNode(child.val);
            node.left = encodeChildren(child.children);
            if (head == null) head = node;
            else cur.right = node;
            cur = node;
        }
        return head;
    }
    
    public Node decode(TreeNode data) {
        if (data == null) return null;
        Node nRoot = new Node(data.val, new ArrayList<>());
        nRoot.children = decodeChildren(data.left);
        return nRoot;
    }
    
    private List<Node> decodeChildren(TreeNode node) {
        List<Node> children = new ArrayList<>();
        while (node != null) {
            Node nNode = new Node(node.val, decodeChildren(node.left));
            children.add(nNode);
            node = node.right;
        }
        return children;
    }
}
      
/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children || [];
 * };
 *
 * // Definition for a Binary Tree Node.
 * function TreeNode(val, left, right) {
 *    this.val = (val===undefined ? 0 : val)
 *    this.left = (left===undefined ? null : left)
 *    this.right = (right===undefined ? null : right)
 * }
 */

var Codec = function() {};

Codec.prototype.encode = function(root) {
    if (!root) return null;
    let bRoot = new TreeNode(root.val);
    if (root.children.length > 0) {
        bRoot.left = this._encodeChildren(root.children);
    }
    return bRoot;
};

Codec.prototype._encodeChildren = function(children) {
    let head = null, cur = null;
    for (let child of children) {
        let node = new TreeNode(child.val);
        node.left = this._encodeChildren(child.children);
        if (!head) head = node;
        else cur.right = node;
        cur = node;
    }
    return head;
};

Codec.prototype.decode = function(data) {
    if (!data) return null;
    let nRoot = new Node(data.val, []);
    nRoot.children = this._decodeChildren(data.left);
    return nRoot;
};

Codec.prototype._decodeChildren = function(node) {
    let children = [];
    while (node) {
        let nNode = new Node(node.val, this._decodeChildren(node.left));
        children.push(nNode);
        node = node.right;
    }
    return children;
};
      

Problem Description

The "Encode N-ary Tree to Binary Tree" problem asks you to implement a pair of functions: one to encode an N-ary tree into a binary tree, and another to decode it back. An N-ary tree is a tree where each node can have any number of children, while a binary tree node can have at most two children (left and right).

You are provided with the root node of an N-ary tree and must write:

  • encode(root): Converts the N-ary tree to a binary tree.
  • decode(data): Converts the binary tree back to the original N-ary tree.
The encoding and decoding must be lossless, meaning decoding the encoded tree should result in a tree identical to the original. There is no restriction on the structure of the binary tree except that it must be possible to recover the N-ary tree from it.

Thought Process

The main challenge is to represent the flexible structure of an N-ary tree using the limited structure of a binary tree. In an N-ary tree, each node can have an arbitrary number of children, but in a binary tree, each node can have only two children.

At first, one might consider storing all children in the left and right pointers directly, but this only supports two children per node. To handle more, we need a way to "chain" children together. A classic approach is the "left-child, right-sibling" representation:

  • The first child of an N-ary node becomes the left child in the binary tree.
  • Subsequent siblings are linked via the right pointer.
This way, all children can be traversed by following the left pointer for the first child and then following the right pointers for each sibling.

The key is to maintain this mapping during both encoding and decoding, ensuring the process is reversible.

Solution Approach

We use the "left-child, right-sibling" technique to encode and decode the trees.

  1. Encoding (N-ary to Binary):
    • If the N-ary root is null, return null.
    • Create a binary tree node with the same value as the N-ary node.
    • If the N-ary node has children, encode its children as follows:
      • The first child is encoded and assigned to the binary node's left pointer.
      • Each subsequent child is encoded and linked as the right child of the previous binary node.
    • Recursively apply this encoding for each child.
  2. Decoding (Binary to N-ary):
    • If the binary data is null, return null.
    • Create an N-ary node with the same value as the binary node.
    • Decode the binary node's left child; this gives the first N-ary child.
    • Follow the right pointers to decode all siblings, adding each as a child to the N-ary node.
    • Recursively decode each subtree.

This approach ensures that the structure is preserved and that the process is fully reversible.

Example Walkthrough

Consider the following N-ary tree:

  • Root node: 1
  • Children: 2, 3, 4
  • Node 3 has children: 5, 6

Encoding:

  1. Node 1 becomes the binary root.
  2. Node 2 (first child) is encoded as the left child of node 1.
  3. Node 3 is encoded as the right child of node 2.
  4. Node 4 is encoded as the right child of node 3.
  5. Node 3's children (5 and 6): 5 is the left child of 3, 6 is the right child of 5.

The resulting binary tree structure:

  • 1
    • left: 2
      • right: 3
        • left: 5
          • right: 6
        • right: 4

Decoding:
  1. Start at binary root (1), create N-ary node 1.
  2. Follow left pointer to 2, add as first child.
  3. Follow right to 3, add as next child.
  4. For 3, follow left to 5, add as child; follow right to 6, add as next child.
  5. Continue following right to 4, add as next child of root.

The reconstructed N-ary tree matches the original.

Time and Space Complexity

Brute-force approach: If you tried to store all children in a binary tree node, you'd lose children beyond two, making it impossible to reconstruct the original tree. This is not feasible.

Optimized approach (left-child, right-sibling):

  • Each node in the N-ary tree is visited exactly once during encoding and decoding.
  • For each node, we create a corresponding binary node (or vice versa).
  • Thus, both encoding and decoding take O(N) time, where N is the total number of nodes.
  • Space complexity is also O(N) due to recursion stack and the new tree structures created.

Summary

The key insight is to use the left-child, right-sibling technique to represent an N-ary tree as a binary tree. By chaining children using the left and right pointers, we can encode and decode trees of arbitrary degree efficiently and losslessly. This approach is both elegant and practical, and it demonstrates how clever data structure representations can overcome apparent structural limitations.