二叉树的序列化和反序列化
1.什么是序列化和反序列化
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
简而言之:
1.我们二叉树把转化为数组或字符串来进行存储的过程就叫二叉树的序列化。
2.我们通过转化的数组或字符串来还原出二叉树的过程叫二叉树的反序列化。
下面我们来看看如何去序列化和反序列化:
2.如何来序列化和反序列化
序列化的方式有很多种。我们知道二叉树的还原,由二叉树的中序序列和前 / 后序序列,或者中序序列和层序序列可以唯一地还原一棵二叉树。 而且我们必须包含中序序列才能唯一地还原一棵二叉树。 我们来看看下面这两棵树:
我们可以看到这两棵树的前序,中序,后序,包括层序都没有任何的区别,那我们如何去序列化呢?
我们把他们所有的空指针表示出来就可以了,看下面这张图。
我们现在再看看图一的前序为:[1,null,1,1,null]
图二的前序:[1,1,null,1,null]
我们发现加上空指指针后,两者的前序有了区别,所以就可以通过前序来序列化,这样每棵树的前序就是唯一的。
2.1前序进行序列化和反序列化
现在我们来看看下面这颗树前序如何序列化和反序列化:
它的前序序列化结果为:[1,2,4,null,3,5,null]
其实就是前序的递归顺序,我们可以通过递归来得出序列化结果,下面是代码
void preserial(Node*root,queue<string>&ans)
{
//保存节点
if (root == NULL)
{
ans.push("null");
return;
}
ans.push(to_string(root->val));
preserial(root->left, ans);
preserial(root->right, ans);
}
反序列化也是一样的操作,通过递归去实现
Node* prebuildtree(queue<string>& q)
{
string s = q.front();
q.pop();
if (s == "null")
{
return NULL;
}
Node* head = new Node(stoi(s));
head->left = prebuildtree(q);
head->right = prebuildtree(q);
return head;
}
2.2为啥不可以用中序序列化
当时以为中序也可以序列化,但是发现序列化之后。自己尝试反序列化的时候,好像无法通过递归再去把树建出来(可能是自己太菜了),看了力扣上这个题,题解也没有用中序序列化的。
之后发现用中序序列化,不同的树可能对应一个序列化,这样就不是一一对应了,就不可以反序列化了。所以不可以,我们看看下面这两棵树:
我们发现这两棵树的中序序列化结果都是:[null,3,null,2,null,4,null,1,null,5,null]
这样肯定就不可以了,我们是不可以唯一还原出来一颗树的。
2.3后序进行序列化和反序列化
后序的顺序为左右根,我们发现反序列化的时候,我们可以从后往前走,先找到根,再去递归建立右子树,在递归建立左子树。它和前序的递归还原树的思想是一样的,我们是可以进行反序列化的。
因为我们要从后面往前面访问,为了方便,所以这次用的string,空节点用#表示。下面是代码:
序列化:
void posserial(Node* root, string& ans)
{
if (root == NULL)
{
ans += '#';
return;
}
posserial(root->left, ans);
posserial(root->right, ans);
ans += (root->val + '0');
}
反序列化:
Node* posbuildtree(string &s)
{
char t=s.back();
s.pop_back();
if (t == '#')
{
return NULL;
}
Node* head = new Node(t-'0');
head->right= posbuildtree(s);
head->left= posbuildtree(s);
return head;
}
2.4层序来进行序列化和反序列化
做法一样,把空节点补出来,然后按照层序的方式去保存,下面是代码:
序列化:
queue<string> levelSerial(Node*head)
{
//保存系列以后的结果
queue<string> ans;
//用来层序遍历
queue<Node*> q;
if (head == NULL)
{
ans.push("null");
return ans;
}
q.push(head);
ans.push(to_string(head->val));
while (!q.empty())
{
Node* t = q.front();
q.pop();
if (t->left == NULL)
{
ans.push("null");
}
else
{
ans.push(to_string(t->left->val));
q.push(t->left);
}
if (t->right == NULL)
{
ans.push("null");
}
else
{
ans.push(to_string(t->right->val));
q.push(t->right);
}
}
return ans;
}
反序列化:
Node* getNode(string s)
{
if (s == "null") return NULL;
else
{
return new Node(stoi(s));
}
}
Node* levelbuildtree(queue<string>&ans)
{
if (ans.size() == 0)
{
return NULL;
}
//拿出第一个节点放入q;
queue<Node*> q;
Node*head=getNode(ans.front());
ans.pop();
//看头节点是否为空
if (head != NULL)
{
q.push(head);
}
Node* node = NULL;
while (!q.empty())
{
node = q.front();
q.pop();
//左右子树不管是不是空都要建出来。
node->left = getNode(ans.front());
ans.pop();
node->right = getNode(ans.front());
ans.pop();
//存在左树继续做。
if (node->left != NULL)
{
q.push(node->left);
}
//存在右树也继续。
if(node->right!=NULL)
{
q.push(node->right);
}
}
return head;
}
2.5习题练习
最后给大家留一到力扣的题,来练习巩固一下吧。
3.总结
1.二叉树的序列化的方式有多种,前序后序,层序都是可以的,只有中序不可以。
2.注意:要把空节点补出来,才可以进行。
3.如果不补出来,就是另一类的还原二叉树问题了,要有中序和前 / 后序序列,或者中序序列和层序序列才可以唯一地还原一棵二叉树。