431. Encode N-ary Tree to Binary Tree
Problem Explanation
Here we're going to design an algorithm to encode an N-ary tree into a binary tree and then be able to decode it back to the original N-ary tree. An N-ary tree is a tree where each node has no more than N children. Similarly, a binary tree is one where each node has no more than 2 children. There is no restriction on how your encoding or decoding should work, but it must be able to accomplish accurately converting between the two tree types.
As an example: Consider an N-ary tree with N=3. It contains a root node and multiple other nodes, each node might have 0 up to 3 children. If we encode this tree as a binary tree, each node of the original N-ary tree could be represented as a binary tree node, with the node's children represented as a linked list of binary tree nodes in the left child of that node. The sibling nodes (other nodes on the same level) are then represented by the right child of each previous node.
For example, an N-ary tree node 1 with children 2, 3, 4, would be encoded into a binary tree like so: Node 1's children are represented as Node 2 (the first child) being the left child of Node 1, Node 3 being the right child of Node 2, and Node 4 being the right child of Node 3.
Solution Explanation
The provided solution encodes N-ary trees as binary trees by taking each node's children and representing them as a linked list in the binary tree. The first child of each N-ary node becomes the left child in the binary tree and other children become the right child of the previous child node.
Decoding is the reverse process. It involves converting the binary tree back to an N-ary tree. The left child of each binary tree node becomes the first child in the N-ary tree, and the right child of each binary tree node becomes the next sibling in the N-ary tree.
The solution uses a queue data structure to traverse both trees in level order and a similar approach using two while loops in both encoding and decoding methods.
Let's write this solution in different programming languages.
python from collections import deque class Node: def __init__(self, val=None, children=None): self.val = val self.children = children if children else [] class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None class Codec: def encode(self, root): if not root: return None newRoot = TreeNode(root.val) queue = deque([(newRoot, root)]) while queue: parent, curr = queue.popleft() dummy = head = TreeNode(0) for child in curr.children: newNode = TreeNode(child.val) dummy.right = newNode dummy = newNode queue.append((newNode, child)) parent.left = head.right return newRoot def decode(self, data): if not data: return None root = Node(data.val) queue = deque([(root, data)]) while queue: parent, curr = deque.popleft() firstChild = curr.left sibling = firstChild while sibling: newNode = Node(sibling.val) parent.children.append(newNode) queue.append((newNode, sibling)) sibling = sibling.right return root
java import java.util.*; class Node { public int val; public List<Node> children; public Node() {} public Node(int _val,List<Node> _children) { val = _val; children = _children; } }; class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } class Codec { public TreeNode encode(Node root) { if (root == null) return null; TreeNode newRoot = new TreeNode(root.val); if (root.children.size() > 0) { newRoot.left = encode(root.children.get(0)); } TreeNode sibling = newRoot.left; for (int i = 1; i < root.children.size(); ++i) { sibling.right = encode(root.children.get(i)); sibling = sibling.right; } return newRoot; } public Node decode(TreeNode root) { if (root == null) return null; Node newRoot = new Node(root.val, new LinkedList<>()); TreeNode sibling = root.left; while (sibling != null) { newRoot.children.add(decode(sibling)); sibling = sibling.right; } return newRoot; } }
javascript function Node(val,children) { this.val = val; this.children = children; }; function TreeNode(val, left, right) { this.val = (val===undefined ? 0 : val) this.left = (left===undefined ? null : left) this.right = (right===undefined ? null : right) }; class Codec { encode(root) { if (!root) return null; let newRoot = new TreeNode(root.val); if (root.children.length > 0) { newRoot.left = this.encode(root.children[0]); } let sibling = newRoot.left; for (let i = 1; i < root.children.length; ++i) { sibling.right = this.encode(root.children[i]); sibling = sibling.right; } return newRoot; } decode(root) { if (!root) return null; let newRoot = new Node(root.val, []); let sibling = root.left; while (sibling !== null) { newRoot.children.push(this.decode(sibling)); sibling = sibling.right; } return newRoot; } }
c++ #include <vector> #include <queue> using namespace std; class Node { public: int val; vector<Node*> children; Node() {} Node(int _val) { val = _val; } Node(int _val, vector<Node*> _children) { val = _val; children = _children; } }; class TreeNode { public: int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; class Codec { public: TreeNode* encode(Node* root) { if (!root) return NULL; TreeNode* newRoot = new TreeNode(root -> val); if (!root -> children.empty()) newRoot -> left = encode(root -> children[0]); TreeNode* sibling = newRoot -> left; for (int i=1; i<root->children.size(); i++) { sibling -> right = encode(root -> children[i]); sibling = sibling -> right; } return newRoot; } Node* decode(TreeNode* root) { if (!root) return NULL; Node* newRoot = new Node(root -> val, {}); TreeNode* sibling = root -> left; while (sibling != NULL) { newRoot -> children.push_back(decode(sibling)); sibling = sibling -> right; } return newRoot; } };
The above solutions in Python, Java, and JavaScript, and C++ effectively demonstrate how to solve the problem of encoding and decoding an N-ary tree into and from a binary tree. While each language has its unique syntax and some methods might not be directly transferrable from one language to another, all these solutions hold the same fundamental logic and structure that makes them valid solutions.
In Python, Java, JavaScript, and C++, classes are defined to represent the nodes in the N-ary and binary trees. They also include a Codec class that has encode and decode methods that perform the conversion of trees, and utilize a queue data structure to ease the traversal of nodes.
The encode method starts by checking if the root node exists. If it does not exist, the method returns null, otherwise it creates a new node in the binary tree. It then goes on to convert the children of the current node, if any, into a linked list of binary tree nodes. The first child becomes the left child of the current binary tree node, while the remaining children become the right child of the preceding binary node.
The decode method reverts the encoding process by transforming the linked list of binary tree nodes back into the original N-ary tree. The left child of each binary tree node becomes the first child in the N-ary tree, and all the right children become the next sibling in the N-ary tree.
Despite the differences in syntax and built-in methods among Python, JavaScript, Java, and C++, they all employ the same logic and approach to solving the problem, thus linking these solutions together in their common goal of accurately converting between N-ary and binary trees.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorIs the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!