题目描述
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
题目分析
题目的意思还是比较明确的,首先给定一个树,我们需要先将树转化为序列(serialize),然后再将这个序列转换为树原本的样子(deserialize),调用的形式为deserialize(serialize(root));而这中间的树到序列、序列到树的转换规则由自己决定,只要最终序列化后再反序列得到的结果与最初的树相同即可。
首先即是对树的序列化,对树的序列化有多种方法:前序序列、后序序列、中序序列以及层次序列,序列化的方法都是相类似的,就不多说了,以前序序列为例,即建立一个字符串,对树进行前序遍历,并把遍历结果存放到树中。这里说两点我在这犯了的错误,一个是树上每个结点并非都是个位数,因此在遍历的时候应当将每个结点当做字符串而不是当做字符进行遍历,空节点节点当做“#”或其他标识;除此之外,字符串相互之间需要有间隔符,间隔符的选取一定要注意,“-”短横线是不行的,因为树结点可能是负数,这样间隔符就会与结点有冲突。
然后就是反序列化了,反序列化实际上就是根据前面得到的序列构造树,根据前序序列构造树是比较容易的,需要注意的是,这里能够光通过前序序列构造树的原因是,在最初生成序列的时候将空节点补充为“#”,这样得到的序列实际上是一个完全二叉树的前序序列,完全二叉树只需要一种序列即可。
接下来还需要考虑的是,序列中存在间隔符,因此还应当根据间隔符来将各结点值给读出来,一个比较简单的办法是将各结点值按序读出后放入队列中,接下来以此从队首取出数据来递归构建二叉树即可。
代码如下:
string serialize(TreeNode* root) { //前序遍历构造序列,遇到空节点以“#”代替,以“*”为分隔符
string res;
if(!root)res+="#*";
else
{
res+=to_string(root->val)+"*";
res+=serialize(root->left);
res+=serialize(root->right);
}
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
queue<string>node;
std::stringstream ss(data);
std::string item;
while (getline(ss, item, '*')) //以“*”为终止符从ss中读取字符串到item中,每读取一个item将其压入队列
{
node.push(item);
}
return buildTree(node);
}
TreeNode* buildTree(queue<string>& node){ //前序序列递归建树
string temp=node.front();
node.pop();
if(temp=="#")return NULL;
TreeNode* root=new TreeNode(stoi(temp));
root->left=buildTree(node);
root->right=buildTree(node);
return root;
}