LeetCode 297. Serialize and Deserialize Binary Tree
题目描述
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment.
Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
For example, you may serialize the following tree
1
/ \
2 3
/ \
4 5
as “[1,2,3,null,null,4,5]”, just the same as how LeetCode OJ serializes a binary tree. You do not necessarily need to follow this format, so please be creative and come up with different approaches yourself.
Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.Credits:
Special thanks to @Louis1992 for adding this problem and creating all test cases.
题目分析
这题比较别致。。和其他考算法的题不一样, 这题是考设计。 题目意思很明确: 设计一种方法支持int整数类型的二叉树的储存以及读取。专业点来说,就是 serialize,deserialize
做这道题其实主要是感觉实际意义比较强一点。 (就是工作的时候可能真用的上)。比如我现在想保存一棵好不容易构建出来的二叉树。那么序列化就是最好的办法——序列化转成的形式也就只能是字符串比较好一些了。
现在就是要设计一个二叉树到字符串之间的转换规则。使得能够双向转换。
题目中给出了在LeetCode OJ 上他们是如何处理的。其实方式很粗暴:就是进行一次BFS,对树按层遍历。没有的地方填上NULL
即可。
但是我一直在想一个问题。。 假如这棵树不平衡怎么办呢。 比如,深度到了100?
所以我设计了一个基于先序遍历的做法。大致想法如下:
1. 按照先序遍历走一遍原二叉树。
2. 对于走到的每个点, 将数据(即该点存放的对应int)转为string 加入到序列化变量。
3. 对于每一步的状态转移, 加入控制命令,同样存入序列化变量里。
这个方法核心就是第三步, 这个步骤相当于在
deserialize
的时候起到指示的作用。
对于控制命令, 一共三种:
1. l
: 将当前操作移至左子树进行
2. r
: 将当前操作移至右子树进行
3. b
: 将当前操作返回到上一层
那么下面来上一个例子说明吧:
例如我的二叉树就是上面例子的那个, 换做BFS的表示方法即:
[1,2,3,null,null,4,5]
用我上面的方法序列化得到的是:1l2br3l4br5bbb
虽然,看着乱,但是很实用!
构建出了这样的序列之后, 只需要很简单的解码过程——这正是我在序列化设计的时候追求的:要让反序列化的过程尽可能简单。
反序列化的操作, 我就以上面那个例子说明吧。
1. 首先对于一开始, 识别出是操作数1
,将操作数值置为当前节点的值。(起始即为根节点)
2. 对于下一个符号, 识别出是操作符l
, 将节点移动至当前节点的左子节点操作。
3. 对于下一个符号, 识别出是操作数2
, 将当前节点的值置为2
4. 对于下一个符号, 识别出是操作符b
, 直接返回。(递归写法就退出本层即可)
5. 下一个操作符r
,就进入当前节点的右子树操作
6. ……
可以明显的看到, 有操作符指导二叉树的重建, 整个过程非常简单。
下面是实现代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
int num(string & s, int & index) {
bool neg = false;
if (s[index] == '-') {
++index;
neg = true;
}
int ans = 0;
while (s[index] >= '0' && s[index] <= '9') {
ans = ans * 10 + s[index] - '0';
++index;
}
if (neg) ans = -ans;
return ans;
}
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string mytree = "";
if (root != NULL) buildString(root, mytree);
return mytree;
}
void buildString(TreeNode * root, string &mytree) {
if (root != NULL) mytree += str(root->val);
if (root->left == NULL && root->right == NULL) {
mytree+= 'b'; //back
return;
}
if (root->left != NULL) {
mytree += 'l';
buildString(root->left, mytree);
}
if (root->right != NULL) {
mytree+= 'r';
buildString(root->right, mytree);
}
mytree+= 'b';
return;
}
string str(int k){
bool neg = false;
if (k < 0) {
k = -k;
neg = true;
}
string ans = "";
while (k > 0) {
ans.insert(ans.begin(), k % 10 + '0');
k/= 10;
}
if (neg) ans.insert(ans.begin(), '-');
return ans;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int index = 0;
return buildTree(data, index);
}
TreeNode * buildTree(string data, int &index) {
if (data.size() == 0) return NULL;
auto node = new TreeNode(num(data, index));
if (data[index] == 'l') {
++index;
node->left = buildTree(data, index);
}
if (data[index] == 'r') {
++index;
node->right = buildTree(data, index);
}
if (data[index] == 'b') {
++index;
return node;
}
return node;
}
};
其他细节
这种方法用来做这道题感觉很不错, 但是遇到二叉树储存的数据结构是比较复杂的话, 用起来就比较不顺了。
这道题还有一个思路, 即参考之前一道题, 由二叉树前序,中序遍历构建二叉树。 按照这个思路, 只需要保存下前序和中序遍历的结果再利用那道题的结论就可以了。