subject
Link:https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/
Problem Description:
analysis
Method 1: DFS (recursive)
Recursion can be understood as: transferring responsibility
- Recurse a tree and only focus on the current single node
- Leave the rest to recursion
- "Serialize function, can you help me serialize my left and right subtrees? I'll wait for your return and add it to me. "
- Why to choose preamble traversal? When deserializing, the root is left and right, which makes it easier to locate the root node value
- When a null node is encountered, it needs to be translated into a special symbol. Only when deserializing can we know the corresponding null node here
Serialization code
const serialize = (root) => { if (root == null) return 'X,' // null node encountered const leftSerialized = serialize(root.left) //Serialization string of left subtree const rightSerialized = serialize(root.right) //Serialized string of right subtree return root.val + ',' + leftSerialized + rightSerialized // Root left right }
Deserialization -- also recursion
Serialization is a pre order traversal, so the serialization string presents the following arrangement:
"Root | (root | (root |, left |, right) | (root | (root |, left |, right) | (root |, left |, right) |"
The function of building tree
- The "status" received by buildTree is a list array, which is converted from a serialized string to
- Follow the sequence of traversal: first build the root node, then the left subtree, then the right subtree
- The first item of the list array is the root node of the current subtree. Pop it up and build it first
buildTree pays attention to the current node, and then transfers the responsibility
- Pop up the first item of the list array and examine it
- If it is' X ', it returns null directly, and there is no subtree to build
- If it's not 'X', create a node for it and build a subtree
- Recursively call buildTree to build the left subtree
- Recursively call buildTree to build right subtree
- The subtree with node as the root node is constructed and returns upward
Deserialization code
const buildTree = (list) => { // dfs function const nodeVal = list.shift() // Nodes of current investigation if (nodeVal == 'X') return null // Yes X, return null to the parent call const node = new TreeNode(nodeVal) // Create node node node.left = buildTree(list) // Building the left subtree of a node node.right = buildTree(list) // Constructing the right subtree of a node return node // Return the subtree with node as root node to parent call } const deserialize = (data) => { const list = data.split(',') // Convert to list array return buildTree(list) // Building trees, entry to dfs }
Complete implementation:
public class Codec { public String rserialize(TreeNode root, String str) { if (root == null) { str += "None,"; } else { str += str.valueOf(root.val) + ","; str = rserialize(root.left, str); str = rserialize(root.right, str); } return str; } public String serialize(TreeNode root) { return rserialize(root, ""); } public TreeNode rdeserialize(List<String> l) { if (l.get(0).equals("None")) { l.remove(0); return null; } TreeNode root = new TreeNode(Integer.valueOf(l.get(0))); l.remove(0); root.left = rdeserialize(l); root.right = rdeserialize(l); return root; } public TreeNode deserialize(String data) { String[] data_array = data.split(","); List<String> data_list = new LinkedList<String>(Arrays.asList(data_array)); return rdeserialize(data_list); } };
Method 2: BFS
Serialization - standard BFS
- I want null to be listed as a real node. It has a corresponding "X", but no child nodes are listed
Investigate the listed nodes - If it is not null, its value is pushed into the res array and its left and right child nodes are listed
- If null, push 'X' into res array
- List List Until the queue is empty, all nodes are traversed, and the res array is constructed, and converted into a string
Serialization code
const serialize = (root) => { const queue = [root] let res = [] while (queue.length) { const node = queue.shift() if (node) { // Listed nodes bring out children to be listed res.push(node.val) queue.push(node.left) // Whether it is a null node or not queue.push(node.right) } else { res.push('X') } } return res.join(',') }
Deserialization -- also BFS: parent node is listed, child node is listed
The following figure shows the serialization string obtained by BFS:
- Except for the first ROOT value, other node values are paired, corresponding to left and right child nodes respectively
- Let's start with the second term and look at two node values at a time
- The node built first is the father of the node built later. Use a queue to temporarily store it
- The queue is initially placed in the ROOT. List the parent node and find the child node
At the same time, the parent node and two child node values are examined
-
The listed parent node, which corresponds to the left child node value pointed by the pointer and the right child node value to the right of the pointer
-
If the child value is not 'X', create a node for it, identify the parent, and list it as a future parent
-
If the child node value is' X ', do nothing (the parent node already has a null child node)
-
All parent nodes (real nodes) will walk once in the queue
Deserialization Code:
const deserialize = (data) => { if (data == 'X') return null // Only one 'X', only one null const list = data.split(',') // Serialized string to list array const root = new TreeNode(list[0]) //The first item is the root node value, create a node for it const queue = [root] // Put it into root initially, and make an investigation later let cursor = 1 // Traverse from the second item in the list while (cursor < list.length) { // Pointer out of bounds const node = queue.shift() // Parent node inspection const leftVal = list[cursor] // Get left child value const rightVal = list[cursor + 1] // Get the value of the right child node if (leftVal !== 'X') { // Left child value is valid const leftNode = new TreeNode(leftVal) // Create node node.left = leftNode // Becomes the left child of the current dequeue node queue.push(leftNode) // It's the father of the future, listed for inspection } if (rightVal !== 'X') { // The right child value is a valid value const rightNode = new TreeNode(rightVal) // Create node node.right = rightNode // Becomes the right child of the current dequeue node queue.push(rightNode) // It's the father of the future. It's on the list } cursor += 2 // Pointer forward 2 bits } return root // Return to root node }
Complete implementation:
public class Codec { // Encodes a tree to a single string. public String serialize(TreeNode root) { if(root==null){ return ""; } StringBuilder res=new StringBuilder(); Queue<TreeNode> queue=new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ TreeNode cur=queue.poll(); if(cur==null){ res.append("null,"); } else{ res.append(String.valueOf(cur.val)); res.append(","); queue.offer(cur.left); queue.offer(cur.right); } } return res.toString(); } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { if(data.equals("")){ return null; } String []temp=data.split(","); TreeNode root=new TreeNode(Integer.valueOf(temp[0])); Queue<TreeNode> queue=new LinkedList<>(); queue.offer(root); TreeNode cur=root; for(int i=1;i<temp.length;){ cur=queue.poll(); if(!temp[i].equals("null")){ cur.left=new TreeNode(Integer.valueOf(temp[i])); queue.offer(cur.left); } i+=1; if(!temp[i].equals("null")){ cur.right=new TreeNode(Integer.valueOf(temp[i])); queue.offer(cur.right); } i+=1; } return root; } }
);
} i+=1; if(!temp[i].equals("null")){ cur.right=new TreeNode(Integer.valueOf(temp[i])); queue.offer(cur.right); } i+=1; } return root; }
}