C++ 序列化与反序列化二叉树
简介
1、题目要求2、题目说明3、核心问题4、解题思路5、代码实现6、问题扩展
1、题目要求
2、题目说明
序列化的意思是指将一些特定的数据结构,变成有格式信息的字符串。例如对一个链表,可以将1->2->3->4->NULL序列化为"1,2,3,4"。对于序列化算法,必须支持反序列化,即在约定的格式下,可以将满足格式要求的字符串重新构造为原始的结构形式。
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果string,重构出原始的二叉树结构。
3、核心问题
拿到这个题目,我们面临着两个至关重要的问题:
1、题目是要求序列化二叉树,对于二叉树这种数据结构,很可能存在着空节点,那么,对于空节点,该怎么表示呢?
2、题目要求实现二叉树的反序列化,我们按照将二叉树序列化为一个字符串的前提,对于一个字符串,我们怎么区分出每个独立的二叉树的节点呢?
以上两个问题,在很多刷题网站会直接给出结论或者结果,这样降低了题目的难度,但作为面试者需要清楚,即使没有提示我们也要注意到这两点问题。即空节点用特殊字符"#"表示,节点之间用逗号“,”分割。也就是反序列化的时候遇到#那么就表示该节点为空节点,可以结束该字符的遍历了。而拆分节点的时候我们只需要取出两个逗号之间的数据即是该节点的值。当然,这里也可以用其他的符号代替"#"号和","号,只要在反序列化的时候做好对应处理就可以了。
4、解题思路
1.序列化二叉树,借助string类型来拼接序列化的字符串。只需要前序遍历二叉树,当遇到根节点为空时,追加"#",并退出递归,否则追加二叉树的根节点值,接着追加逗号。接下来只需递归实现左子树和右子树的序列化。那么,序列化之后就得到了string类型的字符串。
2.反序列化二叉树,如果string为空,则直接返回退出。如果遇到"#",则为空节点,退出递归。否则,找到字符数组中的一个二叉树节点值,然后构造根节点,如果此时到达字符末尾,直接返回根节点,否则继续遍历。接下来根结点的左右指针分别连接左子树的递归实现结果和右子树的递归实现结果。
5、代码实现
//二叉树结构
struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}
};
//序列化二叉树
//to_string将每个结点的val值转化为string类型存储在str上。
void Serialize(TreeNode* node, string& s)
{
if (node == nullptr)
{
s.push_back('#');
s.push_back(',');
return;
}
s += to_string(node->val);
s.push_back(',');
Serialize(node->left, s);
Serialize(node->right, s);
}
//反序列化二叉树
TreeNode* Deserialize(string& s)
{
if (s.empty())
return nullptr;
if (s[0] == '#')
{
s = s.substr(2);
return nullptr;
}
TreeNode* node = new TreeNode(stoi(s));
s = s.substr(s.find_first_of(',') + 1);
node->left = Deserialize(s);
node->right = Deserialize(s);
return node;
}
6、问题扩展
问题1. 将题目中的节点值类型由int换为string,我们还能用"#"来表示空节点吗?还能用","来分割节点吗?
问题2. Serialize里使用了"+=",他的复杂度是怎样的,请分析一下?
提示一下,问题1,解决的思路类似于网络请求中请求参数的处理。问题2,主要是考察了string的操作符重载的过程。