1、题目描述
https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/
2、解法
这里使用后序遍历解法:
我们知道仅仅知道一种遍历结果是无法还原一颗二叉树的,但是现在我们只需要序列化的时候,把空指针也序列化(如使用#表示)则我们是可以还原一个树出来的。
2.1、后序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
StringBuilder sb = new StringBuilder();
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
serializeR(root);
return sb.toString();
}
public void serializeR(TreeNode root){
if(root==null){
sb.append("#"+",");
return ;//#表示null结点
}
serializeR(root.left);
serializeR(root.right);
//后序序列化
sb.append(root.val+",");
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
//这是后序序列,则最后一个结点是根结点,而且这个序列的元素个数
//满足完全二叉树
LinkedList<String>nodeList = new LinkedList<>();
String[]nodes = data.split(",");
//将序列整合为结点集合
for(String s:nodes){
nodeList.addLast(s);
}
return deserialize(nodeList,nodes.length-1);
}
/*
*length为元素下标索引最大值
*/
private TreeNode deserialize( LinkedList<String>nodeList,int length){
if(nodeList==null||length<0) return null;
String s = nodeList.removeLast();
TreeNode root=null;
if(s.equals("#")) {
return root;
}
else root = new TreeNode(Integer.parseInt(s)); //最后一个元素是root,紧接着就是它的左右子树结点
root.right = deserialize(nodeList,length-1);//先右再左
root.left = deserialize(nodeList,length-2);
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));
2.1、前序遍历解法
前序遍历中,第一个结点就是根结点的位置,我们只需要知道这个,然后稍微修改上面的代码即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
StringBuilder sb = new StringBuilder();
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
serializeR(root);
return sb.toString();
}
public void serializeR(TreeNode root){
if(root==null){
sb.append("#"+",");
return ;//#表示null结点
}
//前序序列化
sb.append(root.val+",");
serializeR(root.left);
serializeR(root.right);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
//这是前序序列,则最后一个结点是根结点,而且这个序列的元素个数
//满足完全二叉树
LinkedList<String>nodeList = new LinkedList<>();
String[]nodes = data.split(",");
//将序列整合为结点集合
for(String s:nodes){
nodeList.addLast(s);
}
return deserialize(nodeList);
}
/*
*length为当前序列的长度
*/
private TreeNode deserialize( LinkedList<String>nodeList){
if(nodeList==null||nodeList.size()==0) return null;
String s = nodeList.removeFirst();//第一个为root
TreeNode root=null;
if(s.equals("#")) {
return root;
}
else root = new TreeNode(Integer.parseInt(s)); //最后一个元素是root
root.left = deserialize(nodeList);
root.right = deserialize(nodeList);
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));
运行结果:
2.3、中序遍历
无法知道根结点的位置所以无法恢复序列化,而只能进行序列化
2.4、层级遍历——广度优先遍历
二叉树的广度优先遍历又称按层次遍历。算法借助队列实现。
首先我们先回顾一下层级遍历的代码框架:
利用队列的先进先出的特点,从根开始,依次向下,对于每一层从左向右遍历。
void traverse(TreeNode root){
if(root==null) return null;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);//第一层只有一个结点
while(!q.isEmpty()){
//队列不空
TreeNode cur = q.poll();//将队头取出
//遍历代码
//
//
//完成一个结点的遍历,加入下一层
if(cur.left!=null){
//加入左
q.offer(cur.left);
}
if(cur.right!=null){
//加入右
q.offer(cur.right);
}
}
}
标准的二叉树层级遍历,所以我们很容易就能写出层级序列化的代码(要记录空指针):
String SEP = ",";
String NULL = "#";
/* 将二叉树序列化为字符串 */
String serialize(TreeNode root) {
if (root == null) return "";
StringBuilder sb = new StringBuilder();
// 初始化队列,将 root 加入队列
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode cur = q.poll();
/* 层级遍历代码位置 */
if (cur == null) {
sb.append(NULL).append(SEP);
continue;
}
sb.append(cur.val).append(SEP);
/*****************/
q.offer(cur.left);
q.offer(cur.right);
}
return sb.toString();
}
那么这个时候层序遍历的序列有什么规律呢?每一层的个数按照完全二叉树方式递增。所以我们反序列化的时候,我们需要记录对应的子节点的位置。第一个是root,第二个的是root.left…这个代码看起来有点难,其实我们是一层一层的添加的,使用队列。
/* 将字符串反序列化为二叉树结构 */
TreeNode deserialize(String data) {
if (data.isEmpty()) return null;
String[] nodes = data.split(SEP);
// 第一个元素就是 root 的值
TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
// 队列 q 记录父节点,将 root 加入队列
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
//层级遍历中恢复序列
for(int i=1;i<nodes.length;){
TreeNode parent = q.poll();
//现在取出当前父节点的下一层的左右子结点
// 父节点对应的左侧子节点的值
String left = nodes[i++];
//恢复
if (!left.equals(NULL)) {
//-------框架
parent.left = new TreeNode(Integer.parseInt(left));
q.offer(parent.left);//----------层序遍历代码
} else {
parent.left = null;//空指针不需要加入
}
// 父节点对应的右侧子节点的值
String right = nodes[i++];
if (!right.equals(NULL)) {
//-------框架
parent.right = new TreeNode(Integer.parseInt(right));
q.offer(parent.right);//层序遍历代码
} else {
parent.right = null;//空指针不需要加入
}
}
return root;
}
运行结果: