树节点在内存中存放,通过指针串接起来,现在想把这棵树变成文件的形式(那么最好把这棵树转成字符串);这个字符串可以代表唯一的树结构,还可以还原出唯一的树来,这就叫二叉树的序列化和反序列化 。二叉树可以通过先序、后序或者按层遍历的方式序列化和反序列化,但是二叉树不能通过中序遍历实现序列化和反序列化,因为不同的二颗树可能得到相同的中序遍历结果。
二叉树的节点定义如下:
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
1.通过先序遍历实现二叉树的序列化和反序列化
//1. 先序遍历进行二叉树序列化
public static Queue<String> preSerial(Node head) {
Queue<String> ans = new LinkedList<>();
pres(head, ans);//递归的过程中把整棵树序列化的结果填到ans队列中去
return ans;
}
public static void pres(Node head, Queue<String> ans) {
//base case
if (head == null) {
ans.add(null); //在队列中加null占位
//规定空用null在队列中占位(也可以用特殊字符比如#)
} else {
ans.add(String.valueOf(head.value));//非空把自己的值序列化为字符串加入到队列中
pres(head.left, ans); //然后递归序列化左树
pres(head.right, ans); //递归序列化右树
}
}
//1.先序遍历的反序列化
public static Node buildByPreQueue(Queue<String> prelist) {
//给一个字符串类型的队列建出整棵树然后返回头结点
if (prelist == null || prelist.size() == 0) {
return null;
}
return preb(prelist);
}
public static Node preb(Queue<String> prelist) {
String value = prelist.poll(); //从队列弹出元素
//base case
if (value == null) { //如果这个元素是空,那么就建立一个null的二叉树节点返回
return null;
}
//字符串不是空
Node head = new Node(Integer.valueOf(value)); //把字符串转成整型然后建出节点,作为此时的头结点
//因为序列化时候是先序:头左右。所以反序列化是先让左树消费队列,再让右树消费队列,最后返回头结点。
head.left = preb(prelist);
head.right = preb(prelist);
return head;
}
2.通过层次遍历实现二叉树的序列化和反序列化
//层次遍历序列化二叉树 :需要2个队列
public static Queue<String> levelSerial(Node head) { //给一棵树的头结点返回字符串组成的队列
Queue<String> ans = new LinkedList<>(); // string类型的队列作为序列化后的返回值
if (head == null) {
ans.add(null); //如果是空树那么队列中加上null后直接返回
} else { //非空
ans.add(String.valueOf(head.value)); //先把头结点的值序列化为字符串加到队列中
Queue<Node> queue = new LinkedList<Node>(); //Node组成的队列,因为要按层次遍历
queue.add(head); // 头结点入队
while (!queue.isEmpty()) { //队列不为空就继续循环
head = queue.poll(); // 此时的head理解为父,每一个父去序列化它的子 ;
//弹出父的时候,左边不为空,左边进行序列化;右边不为空,右边进行序列化
if (head.left != null) { //左边不为空,在进队的时候就进行序列化
ans.add(String.valueOf(head.left.value)); //左边序列化
queue.add(head.left); //左边孩子入队
} else {
ans.add(null);//左边孩子是空,只进行序列化,但不入队。
}
if (head.right != null) {
ans.add(String.valueOf(head.right.value));
queue.add(head.right);
} else {
ans.add(null);
}
}
}
return ans;
}
//按照层次遍历反序列化二叉树
public static Node buildByLevelQueue(Queue<String> levelList) {
if (levelList == null || levelList.size() == 0) {
return null;
}
Node head = generateNode(levelList.poll());
//因为层次遍历序列化二叉树是先序列化头,所以反序列化也要先反序列化头
Queue<Node> queue = new LinkedList<Node>(); //建立队列,开始为空
if (head != null) {
//判断head是否为空,因为如果head为空表示字符串队列开始第一个字符串为空那么即队列只有一个元素null,直接返回head
//head非空,把head加入到队列中去,继续进行反序列化
queue.add(head);
}
Node node = null;
while (!queue.isEmpty()) {
node = queue.poll();
//当时序列式是让父节点去序列化子节点,所以现在让父节点反序列化子节点。
node.left = generateNode(levelList.poll());
node.right = generateNode(levelList.poll());
//当初序列化时是不空进队列,所以现在反序列化也是不空进队列
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
return head;
}