请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 “[1,2,3,null,null,4,5]”
序列化、反序列化不能有状态,因此,所有信息都必须被序列化到字符串中,如果一个树节点的左、右孩子节点为null,则该信息也必须序列化到字符串中。
//leetcode submit region begin(Prohibit modification and deletion)
import java.util.LinkedList;
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Codec {
String nullNode = "#";
String seperator = ",";
// 先根遍历方式序列化二叉树
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null) return null;
StringBuilder sb = new StringBuilder();
sb.append(root.val);
serialize(root.left, sb);
serialize(root.right, sb);
return sb.toString();
}
public void serialize(TreeNode root, StringBuilder sb){
if(root == null ){
sb.append(seperator).append(nullNode);
return;
}
sb.append(seperator).append(root.val);
serialize(root.left, sb);
serialize(root.right, sb);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data == null ) return null;
String[] vals = data.split(seperator);
LinkedList<String> valueList = new LinkedList<>();
for(String s: vals){
valueList.addLast(s);//将元素添加到列表尾部
}
return deserialize(valueList);
}
TreeNode deserialize(LinkedList<String> valueList){
if(valueList.size() == 0) return null;
//删除并返回第一个元素
String value = valueList.removeFirst();
if(nullNode.equals(value)){
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(value));
root.left = deserialize(valueList);
root.right = deserialize(valueList);
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));
//leetcode submit region end(Prohibit modification and deletion)
前序遍历+队列实现
前序遍历框架:根左右,建立二叉树的序列化结构;反序列时可将得到的字符串存储在队列中,为什么要用队列?
因为得到的序列化字符串按根左右的顺序,故按照先进先出的原则,在反序列化时,用到队列可以很方便的重建二叉树
/**
* 前序遍历的序列化
*/
public static String serialByPre(Node head){
if (head == null){
return "#!";
}
String res = head.value + "!";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
/**
* 前序遍历的反序列化
*/
//把数组加到队列中去
public static Node reconByPreString(String preStr){
//把字符串进行分割
String[] values = preStr.split("!");
Queue<String> queue = new LinkedList<String>();
for (int i = 0; i < values.length; i++) {
queue.offer(values[i]);
}
return reconPreOrder(queue);//将队列中的数组元素进行反序列化
}
private static Node reconPreOrder(Queue<String> queue) {
String value = queue.poll();
//判断空树
if (value.equals("#")){//如果一开始取出来的数就是 # ,则该树为空树
return null;
}
Node head = new Node(Integer.valueOf(value));
head.left = reconPreOrder(queue);//依次反序列化左子树
head.right = reconPreOrder(queue);//依次反序列化右子树
return head;
}
后序遍历框架:左右根,建立二叉树的序列化结构;反序列时可将得到的字符串存储在堆中,为什么要用堆数据结构存储?
因为得到的序列化字符串按左右根的顺序,按照后进先出的原则,可以先顺利找到根节点,只有根节点建立起来,才能有左右子树。故在反序列化时,用到堆可以很方便的重建二叉树
/**
* 层序遍历的序列化过程
*/
public static String serialByLevel(Node head){
if (head == null){
return "#!";
}
String res = head.value + "!";
Queue<Node> queue = new LinkedList<Node>();
queue.offer(head);
while (!queue.isEmpty()){
head = queue.poll();
if (head.left != null){
res += head.left.value + "!";
queue.offer(head.left);
}else {
res += "#!";
}
if (head.right != null){
res += head.right.value + "!";
queue.offer(head.right);
}else {
res += "#!";
}
}
return res;
}
/**
* 层序遍历的反序列化
*/
public static Node reconByLevelString(String levelStr){
String[] values = levelStr.split("!");
int index = 0;
Node head = geneateNodeByString(values[index++]);
Queue<Node> queue = new LinkedList<Node>();
if (head != null){
queue.offer(head);
}
Node node = null;
while (!queue.isEmpty()){
node = queue.poll();
node.left = geneateNodeByString(values[index++]);
node.right = geneateNodeByString(values[index++]);
if (node.left != null){
queue.offer(node.left);
}
if (node.right != null){
queue.offer(node.right);
}
}
return head;
}
private static Node geneateNodeByString(String value) {
if (value.equals("#")){
return null;
}
return new Node(Integer.valueOf(value));
}