方法一:递归
1.解题思路
题意:
二叉树的序列化:给定二叉树,将其结点的遍历序列保存下来,可用前序/中序/后序/层序,保存为字符串。
二叉树的反序列化:接着利用这个字符串再构建回原来的二叉树。
相关知识:
- 前序遍历模板
void preorder(node* root){ if(root==NULL)return;//到达空树,递归边界 printf("%d\n",root->data); preorder(root->lchild);//访问左子树 preorder(root->rchild);//访问右子树 }
- 中序遍历模板
void inorder(node* root){ if(root==NULL)return;//到达空树,递归边界 inorder(root->lchild);//访问左子树 printf("%d\n",root->data); inorder(root->rchild);//访问右子树 }
- 后序遍历模板
void postorder(node* root){ if(root==NULL)return;//到达空树,递归边界 postorder(root->lchild);//访问左子树 postorder(root->rchild);//访问右子树 printf("%d\n",root->data); }
- 层序遍历模板
struct node{ int data; int layer; node* lchild; node* rchild; }; void layerorder(node* root){ queue<node*>q; root->layer=1; q.push(root);//将根结点地址入队 while(!q.empty()){ node* now=q.front();//取出队首元素 q.pop(); printf("%d",now->data);//访问队首元素 if(now->lchild!=NULL){ now->lchild->layer=now->layer+1; q.push(now->lchild);}//左子树非空 if(now->rchild!=NULL){ now->rchild->layer=now->layer+1; q.push(now->rchild);}//右子树非空 } }
2.解法
序列化:采用递归的方式,将大问题划分成小问题,逐个解决。即先深度优先搜索二叉树得到根结点权值,左右孩子非空的时候再分别加上左右孩子结点权值,再不断递归左右,遇到空结点时返回上一层向另一个孩子结点递归。
反序列化:首先将传来的字符串分割,将结点值保存在vector容器内,方便之后取值。接着按照前序遍历建树,取vector内权值新建结点。
3.具体代码
class Solution { public: char* Serialize(TreeNode *root) { if(root==NULL)return "#"; string ans=to_string(root->val)+","+Serialize(root->left)+","+=Serialize(root->right);//字符数组可直接赋值到string char* ans2=new char[ans.size()]; strcpy(ans2,ans.c_str());//字符串转字符数组 return ans2; } vector<string>v;//保存序列分割为各个结点的权值 void init(char* str){//从序列中划分出权值 string s; for(int i=0;i<strlen(str);i++){//将序列分割 if(str[i]!=',')s+=str[i]; else{ v.push_back(s); s.clear(); } } v.push_back(s);//最后一个权值后无',',要记得加上 size=v.size(); } int cur=0,size;//当前结点编号,总的结点数 TreeNode* Deserialize(char *str) { TreeNode* root=NULL; if(!cur)init(str);//初始化,划分下字符串 if(cur<size&&v[cur]!="#"){//当前结点合法且不为NULL root=new TreeNode(stoi(v[cur++]));//新建结点并递归左右 root->left=Deserialize(str); root->right=Deserialize(str); }else cur++;//NULL结点数目也要加加 return root;//返回当前子树根结点 } };
4.时间复杂度和空间复杂度分析
时间复杂度:,n是结点数,需要遍历二叉树所有结点
空间复杂度:,最坏情况下的递归树高度为n,每层一个结点。
方法二:层序遍历
1.解题思路
方法大致与上边类似,只是将DFS变为BFS,先序遍历变为层序遍历。
2.解法
- 序列化:层序遍历当前的二叉树,并用队列保存访问结点权值。在输出了字符串后发现后面拖了一串",#",是遍历最下边一层空结点时得到的,虽然与测试样例不同,但是不影响。
- 反序列化:首先将传来的字符串分割,将结点权值保存进vector容器内,方便之后取值。接着按照层序遍历建树,取vector内权值新建结点。注意当遍历到空结点时,也要令cur++,接着得到下一个结点权值用以新建结点。
3.图解
4.具体代码
class Solution { public: char* Serialize(TreeNode *root) {//序列化 if(root==NULL)return "#"; string ans;//保存最后的序列 queue<TreeNode*>q;//保存结点,用于层序遍历 q.push(root);//根结点入队 while(!q.empty()){//当队列非空时 TreeNode* t=q.front();//取队首元素并出队 q.pop(); if(t==NULL)ans+="#,";//当前指针指向为空结点,添加"#," else{ ans+=to_string(t->val)+",";//加上当前结点 q.push(t->left);//左右孩子入队 q.push(t->right); } } for(int i=ans.size()-1;;i--){//也可以不写这段代码,我这里为了清除序列后多余的"#,",显得序列好看 if(!isdigit(ans[i])){ ans.erase(i,1); }else break; } char *s=new char[ans.size()+1]; strcpy(s,ans.c_str());//string转字符数组接着赋值给s return s; } TreeNode* Deserialize(char *str) {//反序列化 if(str[0]=='#'||str=="")return NULL; vector<string>v;//保存序列分割为各个结点权值 string s; for(int i=0;i<strlen(str);i++){//将序列分割 if(str[i]!=',')s+=str[i]; else{ v.push_back(s); s.clear(); } } v.push_back(s);//最后一个权值后无',',要记得加上 int cur=0,size=v.size();//当前结点编号,总的结点数 queue<TreeNode*>q;//层次遍历建立树 TreeNode* root=new TreeNode(stoi(v[cur++]));//初始化根结点 q.push(root);//根结点入队 while(!q.empty()){ TreeNode* t=q.front();//取队首元素并出队 q.pop(); if(cur<size&&v[cur]!="#"){//为出队结点构造他的左右孩子,并令其孩子也入队 t->left=new TreeNode(stoi(v[cur])); q.push(t->left); } cur++;//结点数++ if(cur<size&&v[cur]!="#"){ t->right=new TreeNode(stoi(v[cur])); q.push(t->right); } cur++;//结点数++ } return root;//返回根结点 } };
5.时间复杂度和空间复杂度分析
时间复杂度:,n为树结点数,遍历过程中需要访问所有结点。
空间复杂度:,需要在队列中保存所有树结点。