The Problem
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
Clarification: The input/output format is the same as how LeetCode serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Examples
Input: root = [1,2,3,null,null,4,5]
Output: [1,2,3,null,null,4,5]
Input: root = []
Output: []
Constraints
- The number of nodes in the tree is in the range [0, 10⁴].
- -1000 <= Node.val <= 1000
Depth-First Search Solution
func serialize(_ root: TreeNode?) -> String {
var res: [String] = []
func dfs(_ root: TreeNode?) {
guard let root = root else {
res.append("nil")
return
}
res.append(String(root.val))
dfs(root.left)
dfs(root.right)
}
dfs(root)
return res.joined(separator: ",")
}
func deserialize(_ data: String) -> TreeNode? {
var values = data.components(separatedBy: ",")
var i = 0
func dfs() -> TreeNode? {
if values[i] == "nil" {
i += 1
return nil
}
let node = TreeNode(Int(values[i])!)
i += 1
node.left = dfs()
node.right = dfs()
return node
}
return dfs()
}
Explanation
The first step to solving this problem is serialize
a node to a string: to do that, we need to find every value in the root
node and decide how to represent null
values. We will use "nil"
for null, as this is the default behavior in Swift. To find the values, we use a depth-first search (DFS) algorithm, adding found values to an array. After DFS completes, we join
the values with a comma
separator.
The second step is to deserialize
the data. To do this, we split
the input string using the same comma
separator we used when serializing the node. We use a DFS algorithm to reconstruct the tree, with one base case to verify if the value is "nil"
.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(n)
Breadth-First Search Solution
func serialize(_ root: TreeNode?) -> String {
if root == nil {
return "nil"
}
var res: [String] = []
var queue: [TreeNode?] = [root]
while !queue.isEmpty {
let node = queue.removeFirst()
if node == nil {
res.append("nil")
} else {
res.append(String(node!.val))
queue.append(node!.left)
queue.append(node!.right)
}
}
return res.joined(separator: ",")
}
func deserialize(_ data: String) -> TreeNode? {
var values = data.components(separatedBy: ",")
if values[0] == "nil" {
return nil
}
let root = TreeNode(Int(values[0])!)
var queue: [TreeNode?] = [root]
var idx = 1
while !queue.isEmpty {
let node = queue.removeFirst()
if values[idx] != "nil" {
node?.left = TreeNode(Int(values[idx])!)
queue.append(node?.left)
}
idx += 1
if values[idx] != "nil" {
node?.right = TreeNode(Int(values[idx])!)
queue.append(node?.right)
}
idx += 1
}
return root
}
Explanation
Another way to solve this problem is by using a breadth-first search (BFS) algorithm. The string representation for null
and the separator remain the same; we only change the underlying mechanism to traverse nodes when we serialize
and find values to create new nodes when we deserialize
the string.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(n)