题目描述
解法一:层序遍历(BFS)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null) return "[]";
StringBuilder res = new StringBuilder();
res.append("[");
// 借助队列实现层序遍历
Queue<TreeNode> q = new LinkedList<> ();
q.offer(root);
while(!q.isEmpty()) {
TreeNode node = q.poll();
if(node != null) {
res.append(node.val + ",");
q.offer(node.left);
q.offer(node.right);
}
else res.append("null,");
}
res.deleteCharAt(res.length()-1); // 删除最末尾的一个逗号
res.append("]");
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null; // 序列中无节点
String[] vals = data.substring(1, data.length()-1).split(","); // 截取字符序列中括号内的部分,并按逗号分隔为多个字符串,转为字符数组。
TreeNode root = new TreeNode(Integer.parseInt(vals[0])); // 创建根结点,节点值为序列中的第一个元素
Queue<TreeNode> q = new LinkedList<> (); // 借助队列实现按层遍历构建树
q.offer(root);
int i = 1; // 借助一个指针指向结点在序列化列表中的对应索引位置,根结点已入队,这里初始化为第一个左子结点的位置即 vals[1]
while(!q.isEmpty()) {
TreeNode node = q.poll();
if(!vals[i].equals("null")) { // 若当前节点的左子节点对应列表中索引位置上的值不为null,说明存在左子树
node.left = new TreeNode(Integer.parseInt(vals[i]));
q.offer(node.left);
}
i++;
if(!vals[i].equals("null")) { // 若当前节点的右子节点对应列表中索引位置上的值不为null,说明存在右子树
node.right = new TreeNode(Integer.parseInt(vals[i]));
q.offer(node.right);
}
i++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
解法二:前序遍历(DFS)
public class Codec {
//原理:我们可以根据前序遍历的顺序来序列化二叉树,因为前序遍历是从根节点开始的。在遍历二叉树碰到 null时,将其序列化为一个特殊的字符(如'$')
//另外,节点的数值之间要用一个特殊字符(如',')隔开,因为节点的值位数不定且正负不定。
//则下面二叉树 1 可以序列化为:
// / \ [1,2,4,$,$,$,3,5,$,$,6,$,$]
// 2 3
// / / \
// 4 5 6
//我们接着以上述字符串为例分析如何反序列化二叉树。第一个读出的数字是1。由于前序遍历是从根节点开始的,这是根节点的值。
//接下来读出的数字是2,根据前序遍历的规则,这是根节点的左子节点的值。同样,接下来的数字4是值为2的节点的左子节点。
//接着从字符串里读出两个字符'$',这表明值为4的节点的左、右子节点均不存在,因此它是一个叶节点。接下来回到值为2的节点,重建它的右子节点。
//由于下一个字符是'$',这表明值为2的节点的右子节点不存在, 2这个节点的左、右子树都己经构建完毕,接下来回到根节点,反序列化根节点的右子树
//下一个序列化字符串中的数字是3,因此右子树的根节点的值为3。它的左子节点是一个值为5的叶节点,因为接下来的三个字符是"5,$,$"。
//同样,它的右子节点是值为6的叶节点,因为最后3个字符是"6,$,$"。
int start=0;//注意这里必须是全局变量,否则后面的迭代过程中start无法正确变化
public String serialize(TreeNode root) {
if(root==null) return "[]";
StringBuilder res = new StringBuilder();
recur(root,res);
res.deleteCharAt(res.length()-1); // 删除最后一个括号
return res.toString();
}
public void recur(TreeNode root, StringBuilder res){ // 前序遍历,递归实现
if(root==null) {
res.append("null,");
return;
}
res.append(root.val+",");// 各元素间用,分割
recur(root.left, res);
recur(root.right, res);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null; // Sting类型判断值是否相等不能用==,要用equal
String[] inputs = data.split(",");
//虽然data中以,结尾,但是上述分割后会默认最后一个,不存在, 不会使最后一个分割元素为空
return build(inputs);
}
public TreeNode build(String[] inputs){
TreeNode res;
if(inputs[start].equals("null")){
start++;
return null; // 若当前节点为null,自然不存在左右节点了,直接返回
}
res = new TreeNode(Integer.parseInt(inputs[start]));
start++;
//注意:start不能以形参的形式引入build方法中,例如build(inputs,start);
//原因是如果是这样,那下面 res.left = build(inputs,start); res.right = build(inputs,start+1); 会由于处于同一级迭代中,start值相同
//但实际上 res.right中应该是上面 res.left迭代完成后才会执行的,start会+1,因此把start作为全局变量较为合适
res.left = build(inputs);
res.right = build(inputs);
return res;
}
}