题目描述
序列化是将一个数据结构或对象转换成二进制序列使其能够存储在文件或内存缓冲区,或者通过网络链接传递。之后可以在相同或另一个计算机环境中重新构造该对象。
设计一个算法实现序列化和反序列化一个二叉树,序列化和反序列化的算法没有限制,只需要保证可以将二叉树序列化为字符串,然后该字符串可以被反序列化为原始的树结构。
注意
- 不得使用类成员、全局或者静态变量存储状态,序列化和反序列化算法不能和状态有关。
样例
你可以将以下二叉树:
1
/ \
2 3
/ \
4 5
序列化为 "[1,2,3,null,null,4,5]"
提示
- 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode序列化二叉树 的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
说明
- 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
算法
(先序遍历序列化) $O(n)$
- 我们按照先序遍历,即可完整唯一的序列化一棵二叉树。但空结点需要在序列化中有所表示。
- 例如样例中的二叉树可以表示为
1,2,(null),(null),3,4,(null),(null),5,(null),(null),
。这里的(null)
仅做示意,并不是真正序列化出字符串(null)
。 - 通过先序遍历序列化该二叉树;反序列化时,按照
,
作为分隔,构造当前结点后分别通过递归构造左右子树。
时间复杂度
- 每个结点仅遍历两次,故时间复杂度为 $O(n)$。
C++ 代码
/**
* 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:
void solve(TreeNode* root, string& s) {
if (root == NULL) {
s += ",";
return;
}
s += to_string(root->val) + ",";
solve(root->left, s);
solve(root->right, s);
}
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string s;
solve(root, s);
return s;
}
TreeNode* decode(int &cur, const string& data) {
if (data[cur] == ',') {
cur++;
return NULL;
}
int nxt = data.find(',', cur);
int val = stoi(data.substr(cur, nxt - cur));
TreeNode *r = new TreeNode(val);
cur = nxt + 1;
r->left = decode(cur, data);
r->right = decode(cur, data);
return r;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int cur = 0;
return decode(cur, data);
}
};
// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));
提问:
大神,想请教一下,反序列化
decode()
中 为什么 遍历data到最后 不用判断if (cur == data.size()) return NULL;
呢?想好像能明白,但是 感觉 还是 有点难以用语言表达出来,请问能 用文字描述一下原因吗?没必要判断,以为保证序列化的字符串就是一颗合法的树,按照规则反序列化不会出问题
好的,谢谢~
难道不会越界?
不会
请问一下为什么要加&符号
& 在参数里是引用,可以看做全局变量
### JS的语法
找到原因了str += “,”不能写成str = str + “,”
求帮忙看下,我这个代码和您的基本一样,为什么总是内存溢出呀
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res = “”;
PreOrderTraversal(root, res);
在反序列化的的时候,不用考虑cur到达字符串尾端的问题吗
合法的字符串不会出现cur越界的情况
decode函数定义的参数中为什么要加一个const呢
说明
data
本身是不可修改的好的
string::find 是 string类的一个成员函数,详情可以参考 string::find。
至于 istringstream 和 ostringstream,我不太清楚,但 string 已经是 C++ 功能比较强大的类了。
int nxt = data.find(‘,’, cur); 这一行代码感觉看得不是很懂诶, 直接用string 会比较好么,看到另外一个解法是用的istringstream 和 ostringstream, 感觉那种看起来会好看一点。不过直接这么用也许有直接用的好处吧
所以用instringstream 怎么写啊??能麻烦上传一下code吗,很好奇想看一下==谢谢
我的代码就用的是istringstream,这里有代码