每日题解:LeetCode 297. 二叉树的序列化与反序列化
题目描述
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
示例:
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 "[1,2,3,null,null,4,5]"
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
解法
```java
/**
* <p>描述: leetcode279 二叉树的序列化与反序列化 </p>
* @author <a href="https://www.kura.ren" target="_blank">kura</a>
* @date: 2020/6/16
*/
public class Codec {
/**
* Codec:: serialize
* <p>序列化二叉树.</p>
* <p>HISTORY: 2020/6/16 created.</p>
* @param root 二叉树
* @return String . 序列化结果
*/
public String serialize(TreeNode root) {
return rserialize(root, "");
}
/**
* Codec:: rserialize
* <p>序列化二叉树.</p>
* <p>HISTORY: 2020/6/16 created.</p>
* @param root 根节点,对于二叉树来说,每个节点都可以是根节点.
* @param str 序列化拼接的字符串.
* @return String 序列化结果
*/
public String rserialize(TreeNode root, String str) {
if (root == null) {
str += "null,";
} else {
str += root.val + ",";
str = rserialize(root.left, str);
str = rserialize(root.right, str);
}
return str;
}
/**
* Codec:: deserialize
* <p>反序列化二叉树.</p>
* <p>HISTORY: 2020/6/16 created.</p>
* @param data 二叉树的字符
* @return TreeNode 反序列完成的二叉树
*/
public TreeNode deserialize(String data) {
String[] arr = data.split(",");
Queue<String> queue = new ArrayDeque<>();
for (String s : arr) {
queue.offer(s);
}
return rdeserialize(queue);
}
/**
* Codec:: rdeserialize
* <p>输出队列中的元素构建二叉树.</p>
* <p>HISTORY: 2020/6/16 created.</p>
* @param queue 队列
* @return TreeNode 由队列构建成的二叉树.
*/
public TreeNode rdeserialize(Queue<String> queue) {
if (queue.peek().equals("null")) {
queue.poll();
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(queue.peek()));
queue.poll();
root.left = rdeserialize(queue);
root.right = rdeserialize(queue);
return root;
}
}
## 解题思路
## DFS
题目要求我们将二叉树转换成字符串表示,同时也能将字符串转换成真正的二叉树。
这里我们使用了DFS遍历二叉树,从题目的例子`[1,2,3,null,null,4,5]`来看,采用了属于层序遍历,当某节点在层次上属于叶节点,其下一层还存在元素,则用null,null。
我们采用前序遍历的方式,使用null,null,左右树的分界线,为什么使用 前序遍历,因为在反序列化时,根|左|右,更容易定位根节点的值,以题目的二叉树为例子
1
/
2 3
/
4 5
1为根节点时![在这里插入图片描述](https://img-blog.csdnimg.cn/2020061621231139.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWhhbzE5Mg==,size_16,color_FFFFFF,t_70)
3为根节点时
在这里插入图片描述![在这里插入图片描述](https://img-blog.csdnimg.cn/20200616212259810.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdWhhbzE5Mg==,size_16,color_FFFFFF,t_70)
那么怎么遍历?
### 序列化二叉树
```java
public String serialize(TreeNode root) {
return rserialize(root, "");
}
public String rserialize(TreeNode root, String str) {
//当节点为空时,字符串添加null,标志
if (root == null) {
str += "null,";
} else {
str += root.val + ",";
//递归
str = rserialize(root.left, str);
str = rserialize(root.right, str);
}
return str;
}
反序列化
由于字符串是以","分割的,我们先将字符串分割成数组,
[1,2,null,null,3,4,null,null,5,null,null],
我们构建二叉树时,从左往右依次输出元素,当遇到null
字符串时,停止当子树遍历,同时,构建一个节点的同时需要删除掉该节点,这个地方符合先入先出,采用了了队列输出按序输出数组元素,数组的删除有点繁琐( System.arraycopy();),所以使用空间换取时间的思路。
public TreeNode deserialize(String data) {
String[] arr = data.split(",");
Queue<String> queue = new ArrayDeque<>();
for (String s : arr) {
queue.offer(s);
}
TreeNode treeNode = rdeserialize(queue);
return treeNode;
}
public TreeNode rdeserialize(Queue<String> queue) {
/null字符表示该子树遍历结束
if (queue.peek().equals("null")) {
queue.poll();
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(queue.peek()));
queue.poll();
root.left = rdeserialize(queue);
root.right = rdeserialize(queue);
return root;
}
这道题目虽然时困难,但是在写法上还是比较清晰的,所以再今天完成每日一题,再对javadoc输出api文档的这块做了点研究,贴图看一下效果,感觉以后同事不会再说我注释少了