题目描述
请实现两个函数,分别用来序列化和反序列化二叉树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
private int index;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
题解
使用先序遍历的方式,将二叉树转换成字符串,将节点中的值用逗号,
隔开。如果遇到null
,则用$
替代。
例如这棵树:
1
/ \
2 3
/ \
4 5
序列化后的字符串是:1,2,$,$,3,4,$,$,5,$,$
。
代码实现:
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
preOrder(root, sb);
return sb.toString();
}
// 先序遍历
private void preOrder(TreeNode node, StringBuilder sb)
{
if (node == null) {
sb.append('$');
return;
}
sb.append(node.val);
sb.append(',');
preOrder(node.left, sb);
preOrder(node.right, sb);
}
然后用相同的方法根据字符串重建树。递归思路可能比较抽象,分析几个例子就能明白。
定义一个变量index
来标识字符串目前读到哪个位置了。
- 如果当前读到的字符是
$
,说明是一个空指针,直接返回null。 - 如果当前读到的是一个非
$
字符,那么就说明是数字,读取整个数字,并用其值创建对象,递归读取字符串给左子树赋值和给右子树赋值。
代码实现:
// 全局变量,用来标识字符串读到哪个位置了
private int index;
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data.charAt(index) == '$') {
index++;
return null;
}
// 读取一个数字
int val = readNumber(data);
// 用读到的数字创建对象
TreeNode node = new TreeNode(val);
node.left = deserialize(data);
node.right = deserialize(data);
return node;
}
// 读取一个数字,如10,-108,5
private int readNumber(String data)
{
int val = 0;
// 正负号
int symbol = 1;
while (data.charAt(index) != ',')
{
if (data.charAt(index) == '-') {
symbol = -1;
} else {
val = val * 10 + data.charAt(index) - '0';
}
index++;
}
index++; // 跳过逗号
return val * symbol;
}
这种顺序读取可行的原因是:先序遍历是先遍历根节点,其次左叶子节点,最后右叶子节点。按照同样的顺序就能处理。
同时,不需要做index
超界判断,因为index走到String最后一个字符后,反序列化函数就刚好重构完二叉树。(这里不考虑字符串非法问题,因为字符串是由序列化函数生成的,所以认为是合法的)
完整代码:
public class Codec {
private int index;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
preOrder(root, sb);
return sb.toString();
}
private void preOrder(TreeNode node, StringBuilder sb)
{
if (node == null) {
sb.append('$');
return;
}
sb.append(node.val);
sb.append(',');
preOrder(node.left, sb);
preOrder(node.right, sb);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data.charAt(index) == '$') {
index++;
return null;
}
// 读取一个数字
int val = readNumber(data);
TreeNode node = new TreeNode(val);
node.left = deserialize(data);
node.right = deserialize(data);
return node;
}
private int readNumber(String data)
{
int val = 0;
// 正负号
int symbol = 1;
while (data.charAt(index) != ',')
{
if (data.charAt(index) == '-') {
symbol = -1;
} else {
val = val * 10 + data.charAt(index) - '0';
}
index++;
}
index++; // 跳过逗号
return val * symbol;
}
}
时间复杂度:
O
(
N
)
O(N)
O(N)。
N
N
N为树的节点个数,每个节点遍历一次即可。
空间复杂度:
O
(
N
)
O(N)
O(N)。
序列化字符串的长度和树节点个数呈线性关系。