You are given the root of an N-ary tree. An N-ary tree is a tree in which each node has no more than N children. Design an algorithm to serialize and deserialize an N-ary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that an N-ary tree can be serialized to a string and this string can be deserialized to the original tree structure.
Clarifications:
Examples:
Example 1:
Input:
1
/ | \
2 3 4
/ \
5 6
Output: Serialized string representing the tree, and a deserialized tree that mirrors the original.
Example 2:
Input: null
(empty tree)
Output: ""
(empty string) and null
.
Constraints:
[0, 10^4]
.0 <= Node.val <= 10^4
A naive approach is to perform a breadth-first traversal of the N-ary tree. We can use special delimiters to indicate the end of a node's children. This is similar to how binary trees are serialized, but we must deal with a variable number of children.
Serialization:
null
to the serialized string when we encounter a null
node.Deserialization:
Consider an N-ary tree where node A has children B and C, and B has child D. A simple serialization could be:
A,2,B,1,C,0,D,0
This denotes that A has value A and 2 children, B has value B and 1 child, C has value C and 0 children, and D has value D and 0 children. However, this lacks clarity and can be difficult to parse robustly, especially with more complex data or node value constraints.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
class Codec:
def serialize(self, root: 'Node') -> str:
if not root:
return ""
result = []
queue = [root]
while queue:
node = queue.pop(0)
if not node:
result.append("null")
else:
result.append(str(node.val))
result.append(str(len(node.children) if node.children else 0))
if node.children:
queue.extend(node.children)
return ",".join(result)
def deserialize(self, data: str) -> 'Node':
if not data:
return None
nodes = data.split(",")
if not nodes:
return None
val = nodes.pop(0)
num_children = int(nodes.pop(0))
root = Node(val, [])
queue = [root]
while queue:
node = queue.pop(0)
for _ in range(num_children):
child_val = nodes.pop(0)
child_num_children = int(nodes.pop(0))
child = Node(child_val, [])
node.children.append(child)
queue.append(child)
if not nodes:
break
val = nodes.pop(0)
num_children = int(nodes.pop(0))
return root
A more robust and efficient approach involves using preorder traversal combined with encoding the number of children each node has. This allows for easier and unambiguous reconstruction of the tree structure.
Serialization:
None
(null) nodes with a specific marker (e.g., '#').Deserialization:
Consider the same N-ary tree as before. A serialized string could be:
A,2 B,1 D,0 C,0
This clearly indicates that A has value A and 2 children, B has value B and 1 child, D has value D and 0 children, and C has value C and 0 children. This representation is easier to parse and less ambiguous than the naive approach.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
class Codec:
def serialize(self, root: 'Node') -> str:
def encode(node):
if not node:
return "# "
s = str(node.val) + "," + str(len(node.children)) + " "
for child in node.children:
s += encode(child)
return s
return encode(root) if root else ""
def deserialize(self, data: str) -> 'Node':
def decode():
if not data:
return None
val, num_children = nodes.pop(0).split(",")
node = Node(int(val), [])
for _ in range(int(num_children)):
node.children.append(decode())
return node
if not data:
return None
nodes = data.split() # Splitting by spaces
if nodes[0] == "#":
return None
return decode()