序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。
二叉树的序列化:一棵二叉树变成字符串形式。
二叉树的反序列化:从字符串形式变成一棵唯一确定的树。
public class SerializeAndReconstructTree {
private static final String SEP = "_"; //分隔符
private static final String NULL = "#"; //代表空节点的字符
/*
前序遍历序列化二叉树
*/
public static String serialByPre(Node head) {
StringBuilder res = new StringBuilder(""); //用于拼接字符串
serialByPre(head, res);
return res.toString();
}
//递归前序遍历的过程中完成序列化
private static void serialByPre(Node head, StringBuilder res) {
if (head == null) { //边界条件
res.append(NULL).append(SEP);
return;
}
res.append(head.value).append(SEP);
serialByPre(head.left, res);
serialByPre(head.right, res);
}
/*
基于前序遍历序列化字符串反序列化
*/
public static Node reconByPreString(String preStr) {
String[] values = preStr.split(SEP);
Queue<String> queue = new LinkedList<>(Arrays.asList(values));
return reconByPreOrder(queue);
}
private static Node reconByPreOrder(Queue<String> queue) {
if (queue.isEmpty()) {
return null;
}
String value = queue.poll();
if (value.equals(NULL)) {
return null;
}
Node head = new Node(Integer.valueOf(value));
head.left = reconByPreOrder(queue);
head.right = reconByPreOrder(queue);
return head;
}
/*
后续遍历序列化
*/
public static String serialByPos(Node head) {
StringBuilder res = new StringBuilder();
serialByPos(head, res);
return res.toString();
}
private static void serialByPos(Node head, StringBuilder res) {
if (head == null) {
res.append(NULL).append(SEP);
return;
}
serialByPos(head.left, res);
serialByPos(head.right, res);
res.append(head.value).append(SEP);
}
/*
基于后续序列化字符串反序列化
*/
public static Node reconByPosString(String posStr) {
String[] values = posStr.split(SEP);
LinkedList<String> list = new LinkedList<>(Arrays.asList(values));
return reconByPosOrder(list);
}
//后续遍历是左右根,根节点在最后,所以要从后往前取出元素构建二叉树,而且要先建右子树再建左子树
private static Node reconByPosOrder(LinkedList<String> list) {
if (list.isEmpty()) {
return null;
}
String value = list.removeLast(); //从后往前取出元素
if (value.equals(NULL)) {
return null;
}
Node head = new Node(Integer.valueOf(value));
head.right = reconByPosOrder(list);
head.left = reconByPosOrder(list);
return head;
}
/*
中序遍历序列化
*/
public static String serialByIn(Node head) {
StringBuilder res = new StringBuilder();
serialByIn(head, res);
return res.toString();
}
private static void serialByIn(Node head, StringBuilder res) {
if (head == null) {
res.append(NULL).append(SEP);
return;
}
serialByIn(head.left, res);
res.append(head.value).append(SEP);
serialByIn(head.right, res);
}
/*
层次遍历序列化
*/
public static String serialByLevel(Node head) {
StringBuilder res = new StringBuilder();
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()) {
Node cur = queue.poll();
if (cur == null) {
res.append(NULL).append(SEP);
continue;
}
res.append(cur.value).append(SEP);
queue.add(cur.left);
queue.add(cur.right);
}
return res.toString();
}
/*
基于层次序列化字符串反序列化
*/
public static Node reconByLevelStr(String levelStr) {
if (levelStr.isEmpty()) {
return null;
}
String[] values = levelStr.split(SEP);
if (values[0].equals(NULL)) {
return null;
}
Node head = new Node(Integer.valueOf(values[0])); //层次遍历序列中第一个值是根节点值
Queue<Node> queue = new LinkedList<>(); //记录父节点
queue.add(head);
for (int i = 1; i < values.length; i++) {
Node parent = queue.poll(); //取出父节点
String left = values[i++]; //取出父节点对应左孩子值
if (!left.equals(NULL)) {
parent.left = new Node(Integer.valueOf(left));
queue.add(parent.left);
} else {
parent.left = null;
}
String right = values[i]; //取出父节点对应右孩子值
if (!right.equals(NULL)) {
parent.right = new Node(Integer.valueOf(right));
queue.add(parent.right);
} else {
parent.right = null;
}
}
return head;
}
}
需要注意的是中序序列化的字符串不能反序列化,因为中序遍历的字符串确定不了根节点的位置。
例如对于下面两个二叉树而言
对应的中序序列化结果均为 #_4_#_2_#_1_#_3_#_5_#_
序列化结果不能唯一确定一棵二叉树,所以不能反序列化。
应用:判断一棵二叉树是不是另一棵二叉树的子树。