题目
算法思路
序列化的结果实际上是二叉树==层序遍历(BFS)==的结果,为了完整表示二叉树,叶节点下的
n
u
l
l
null
null 也要记录下来。
示例中的二叉树各节点和索引的对应关系为:
n o d e . v a l node.val node.val | n o d e node node的索引 | n o d e . l e f t node.left node.left的索引 | n o d e . r i g h t node.right node.right的索引 |
---|---|---|---|
1 | 0 | 1 | 2 |
2 | 1 | 3 | 4 |
3 | 2 | 5 | 6 |
null | 3 | - | - |
null | 4 | - | - |
4 | 5 | 7 | 8 |
5 | 6 | 9 | 10 |
设当前节点 n o d e node node 索引为 n, m 为列表 [0, n] 中的 null 节点数,则根节点、左子节点、右子节点的列表索引的递推公式为:
n o d e . v a l node.val node.val | n o d e node node的索引 | n o d e . l e f t node.left node.left的索引 | n o d e . r i g h t node.right node.right的索引 |
---|---|---|---|
等于null | n | - | - |
不等于null | n | 2 ( n − m ) + 1 2(n-m)+1 2(n−m)+1 | 2 ( n − m ) + 2 2(n-m)+2 2(n−m)+2 |
算法流程
序列化serialize(root):
- 特例处理:若 r o o t root root 为空,直接返回空列表
- 初始化:包含根节点的队列 q u e u e queue queue,和,序列化结果列表 r e s res res
- 层序遍历,当
q
u
e
u
e
queue
queue 为空时跳出:
1.当前节点出队,记为 n o d e node node;
2.若 n o d e node node 非空,打印字符串 n o d e . v a l node.val node.val,并将左右子节点加入队列
3.若 n o d e node node 为空,打印字符串 n u l l null null - 返回值:以","作为分隔拼接列表,首尾添加中括号
反序列化Deserialize(data):
利用推导出的 n o d e node node、 n o d e . l e f t node.left node.left、 n o d e . r i g h t node.right node.right在序列化列表中的位置关系,反推出各节点在序列中的索引。
- 特例处理:若 d a t a data data 为空,直接返回 n u l l null null
- 初始化:序列化列表
v
a
l
s
vals
vals, 指针
i = 1
,根节点 r o o t root root(值设为vals[0]),包含根节点的队列 q u e u e queue queue - 按层构建二叉树,当
q
u
e
u
e
queue
queue 为空时跳出:
1.节点出队,记为 n o d e node node;
2.构建 n o d e node node 的左子节点 n o d e . l e f t node.left node.left,值为 vals[i],并将 n o d e . l e f t node.left node.left 入队,指针 i 后移一位;
3.构建 n o d e node node 的右子节点 n o d e . r i g h t node.right node.right,值为 vals[i],并将 n o d e . r i g h t node.right node.right 入队,指针 i 后移一位。 - 返回值:根节点 r o o t root root
具体代码
/**
* 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("[");//结果列表初始化
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);//队列初始化
//层序遍历
while(!queue.isEmpty()){
TreeNode node = queue.poll();//节点出队
//若当前节点非空
if(node != null){
res.append(node.val+",");//打印node.val
//左右子节点入队
queue.offer(node.left);
queue.offer(node.right);
}else{//当前节点为null
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]));//创建根节点(要先将字符串转换为int型数)
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);//队列初始化
int i = 1;//指针初始化(从1开始是因为vals[0]对应的是根节点)
//按层构建二叉树
while(!queue.isEmpty()){
TreeNode node = queue.poll();//节点出队
if(!vals[i].equals("null")){
node.left = new TreeNode(Integer.parseInt(vals[i]));//构建左子节点
queue.offer(node.left);//左子节点入队
}
i++;//指针i后移一位(无论vals[i]是否为"null"指针都要后移)
if(!vals[i].equals("null")){
node.right = new TreeNode(Integer.parseInt(vals[i]));//构建右子节点
queue.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));
复杂度分析
序列化函数:
- 时间复杂度: O ( n ) O(n) O(n),n 为二叉树的节点数,层序遍历需要访问所有节点,最坏情况下需要访问 n + 1 个 null,总体复杂度为 O ( 2 n + 1 ) = O ( n ) O(2n+1)=O(n) O(2n+1)=O(n)
- 空间复杂度: O ( n ) O(n) O(n),最坏情况下队列会同时存储 n + 1 2 \frac{n+1}{2} 2n+1 个节点,占用 O ( n ) O(n) O(n)
反序列化函数:
- 时间复杂度: O ( n ) O(n) O(n),n 为二叉树的节点数,按层构建二叉树需要遍历整个 v a l s vals vals,长度最大为 2n + 1
- 空间复杂度:最坏情况下队列会同时存储 n + 1 2 \frac{n+1}{2} 2n+1 个节点,占用 O ( n ) O(n) O(n)