297. 二叉树的序列化与反序列化
难度困难
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
示例 1:
输入:root = [1,2,3,null,null,4,5] 输出:[1,2,3,null,null,4,5]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
示例 4:
输入:root = [1,2] 输出:[1,2]
提示:
- 树中结点数在范围
[0, 104]
内 -1000 <= Node.val <= 1000
前序/后序遍历
- 将二叉树转化为字符串-->序列化
- 使用前序遍历(后序遍历也可以,中序遍历不行因为不能找到根节点索引)
- 如果节点为空节点利用#拼接,每个节点利用逗号分隔开(在节点后拼接逗号)
- 如果节点不为空,利用对应val值进行拼接,每个节点利用逗号分隔开(在节点后拼接逗号)
- 将字符串转化为二叉树-->反序列化
- 首先将对应字符串使用split按照,分隔开,就转化为一个字符串数组(数组中就对应字符串形式的二叉树节点值)
- 将对应字符串形式的二叉树节点值 加入到 linkedlist 中,然后进行反序列化
- 接下来就要根据字符串来构造一颗二叉树了,同样也要根序列化统一,使用前序遍历按照顺序构造即可(list一次从头部弹出,弹到空为止)
- 如果list为空则返回null
- 如果弹出字符串为#,返回给父节点为null
- 如果弹出字符串不为#,那就构造对应节点值,返回给父节点
- 接着构造左树,构造右树
- 返回构造好的根节点
public class Codec {
private StringBuilder res = new StringBuilder();
private void serializeDFS(TreeNode root) {
if(root==null) {
res.append("#").append(",");
return;
}
res.append(root.val).append(",");
serializeDFS(root.left);
serializeDFS(root.right);
}
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
serializeDFS(root);
return res.toString();
}
private TreeNode deserializeDFS(LinkedList<String> list) {
if(list.isEmpty()) {
return null;
}
String str = list.removeFirst();
if(str.equals("#")) {
return null;
}
TreeNode root = new TreeNode(Integer.parseInt(str));
root.left = deserializeDFS(list);
root.right = deserializeDFS(list);
return root;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
LinkedList<String> list = new LinkedList<>();
for(String s : data.split(",")) {
list.add(s);
}
return deserializeDFS(list);
}
}
// 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));
层序遍历
- 将二叉树转化为字符串-->序列化
- 使用层序遍历,这次也要将空拼接到字符串中(拼接#),不为空拼接对应值,借点后面拼接逗号
- 将字符串转化为二叉树-->反序列化
- 首先将对应字符串使用split按照,分隔开,就转化为一个字符串数组(数组中就对应字符串形式的二叉树节点值)
- 使用一个队列进行层序遍历(放入节点值)构造一颗二叉树
- 每次从队列中弹出节点作为根节点
- 遍历下一个字符,如果为#,构造左树为null ,如果不为#,构造左树,为左树对应节点值,然后将左树加入到队列中
- 遍历下一个字符,如果为#,构造右树为null ,如果不为#,构造右树,为右树对应节点值,然后将右树加入到队列中
- 直到队列为空,就生成号一颗二叉树
public class Codec {
private StringBuilder res = new StringBuilder();
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root==null) return "";
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int sz = queue.size();
for(int i =0;i<sz;++i) {
TreeNode cur = queue.poll();
if(cur==null){
res.append("#").append(",");
continue;
}
res.append(cur.val).append(",");
queue.offer(cur.left);
queue.offer(cur.right);
}
}
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.length()==0) return null;
String[] str = data.split(",");
TreeNode root = new TreeNode(Integer.parseInt(str[0]));
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
for(int i =1;i<str.length;) {
TreeNode parent = queue.poll();
if(!str[i].equals("#")) {
parent.left = new TreeNode(Integer.parseInt(str[i]));
queue.offer(parent.left);
}else {
parent.left = null;
}
i++;
if(!str[i].equals("#")) {
parent.right = new TreeNode(Integer.parseInt(str[i]));
queue.offer(parent.right);
}else {
parent.right = null;
}
i++;
}
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));
总结 :
使用#进行拼接,使用逗号进行分割开,然后对应着反序列化就按照逗号分隔开转化成字符传数组再加入到list中好进行处理(比如list为empty就知道遍历结束,不用复杂处理字符串).
中序遍历由于找不到根节点索引,无法序列化反序列化