剑指offer之序列化和反序列化二叉树C++解法
题目描述:请实现两个函数,分别用来序列化和反序列化二叉树
序列化:二叉树被记录成文件的过程叫作二叉树的序列化
反序列化:通过文件内容重建原来的二叉树过程叫做二叉树反序列化
原文链接:https://rongxuanhong.github.io/2018/07/15/剑指offer2之序列化二叉树/
我们知道仅有前序遍历是无法确定一颗二叉树的,所以我们还需要要给叶节点的空指针赋予特殊的标记 ‘#’,这样一来,当递归遇到’#’时,我们就知道了该跳出递归了,回溯完之后就进入右子树处理了。
另外,二叉树的节点值可能有两位数,这样就不能单靠指针的移动就能确定二叉树的节点值,还需要加入分割标记,因此我们序列化的时候,需额外追加逗号,分割开不同的二叉树节点值,方便反序列化取出节点值。
方法一:vector
- 借助整型类型的vector数组存储二叉树节点值,所以进行序列化的时候,对二叉树进行前序遍历将节点加入数组,当节点为空的时候,加入特殊数值0xFFFFFFF,并退出递归。由于函数需要返回字符数组类型,因此还需要将vector数组中的元素一一复制到动态整型数组中,最后对整型数组强制转换为字符类型。
- 反序列化时,先将字符数组类型转为整型数组,然后二叉树的节点的值就可以直接从整型数组中得到。首先,处理递归出口,如果遇到0xFFFFFFF,则指针向后移动一个单位并退出递归,否则构造二叉树根节点,并继续移动指针,连接递归得到的左子树以及右子树。
class Solution {
public:
vector<int> buf;
void SerHelper(TreeNode *p){//前序遍历
if(p==NULL)
buf.push_back(0xFFFFFFF);//标记空节点
else{
buf.push_back(p->val);
SerHelper(p->left);
SerHelper(p->right);
}
}
char* Serialize(TreeNode *root) {
buf.clear();
SerHelper(root);
// vector数组转int数组
int size=buf.size();
int *stream=new int[size];
for(int i=0;i<size;i++)
stream[i]=buf[i];
return (char*)stream;//强制类型转换
}
TreeNode* DeserHelper(int*& p){//注意此处需要加引用
if(*p==0xFFFFFFF){
p++;
return nullptr;
}
TreeNode *root=new TreeNode(*p);
p++;
root->left=DeserHelper(p);//递归构造左子树并连接到根节点的左孩子
root->right=DeserHelper(p);//递归构造右子树并连接到根节点的右孩子
return root;
}
TreeNode* Deserialize(char *str) {
int *p=(int*)str;//强制类型转换
return DeserHelper(p);
}
};
方法二:string
- 借助string类型来拼接序列化的字符串。只需要前序遍历二叉树,当遇到根节点为空时,追加‘#’,并退出递归,否则追加二叉树的根节点值,接着追加逗号。接下来只需递归实现左子树和右子树的序列化。那么,序列化之后得到了string类型的字符串,最后构造动态字符数组,将字符串的每一个字符复制到字符数组中,结尾加入’\0’标记结束。
- 进行反序列化时,如果遇到’#’,那么那么指针移动一个单位,并退出递归。否则,找到字符数组中的一个二叉树节点值,然后构造根节点,如果此时到达字符末尾,直接返回根节点,否则指针继续移动一个单位。接下来根结点的左右指针分别连接左子树的递归实现结果和右子树的递归实现结果。
class Solution {
public:
void Serialize(TreeNode *p,string& s){
if(p==NULL){
s+='$';
//return;
}
else{
s+=to_string(p->val);
s+=',';//数字可能有多位,所以一定要加分隔符
Serialize(p->left,s);//追加左子树的字符串
Serialize(p->right,s);//回溯到根节点再追加右子树的字符串
}
}
char* Serialize(TreeNode *root) {
if(root==NULL)
return nullptr;
string s;//借助string拼接字符串
Serialize(root,s);
//string转字符数组
char *stream=new char[s.length()+1];
for(int i=0;i<s.length();i++){
stream[i]=s[i];
}
stream[s.length()]='\0';//加入结尾标记
return stream;
}
TreeNode* Deserialize(char **str){//*str是要操作的指针,**stri是具体指针指向的值,str是二级指针
if(**str=='$'){
++(*str);
return nullptr;
}
int num=0;
while(**str!='\0'&&**str!=','){//循环取出两个逗号之间的数字
num=num*10+(**str-'0');
++(*str);
}
TreeNode *root=new TreeNode(num);
if(**str=='\0')//已经到字符串末尾
return root;
else
++(*str);
root->left=Deserialize(str);//反序列化左子树
root->right=Deserialize(str);//左子树回溯完成后,str已经指向右子树字符串的起始字符
return root;
}
TreeNode* Deserialize(char *str) {
if(str==NULL)
return nullptr;
return Deserialize(&str);
}
};