序列化二叉树:最直观的想法是,使用层序遍历的方式进行存储,对于某个叶子节点的空节点进行存储,同时保证不递归存储空节点对应的子节点。序列化指的是将二叉树转换为字符串;反序列化指的是将字符串转换为二叉树。序列化可以使用层序遍历,如何保证不递归存储空节点的子节点呢?那就是队列中不存储空节点,遇到空节点时只对字符串进行相应的处理,正因如此,应该是入队时处理字符串而不是出队时处理字符串;同时由于数值可能位数不止一位,故需要使用间隔符来分割数值。反序列化可以先根据间隔符分割数值,然后将根节点压入队列,使用变量k标记数组元素下标,初始赋值为根节点的左孩子元素下标,即k=1,当数组不越界时,先从队列中弹出元素,如果当前元素左孩子不为空,则构造左孩子节点,并将左孩子压入队列,同时设置当前元素与其左孩子之间的指针关系,右孩子同理,最后还要对数组元素下标k右移两位,最后返回root即可。即使用队列表示当前元素,保证其非空,使用数组表示当前元素的左右孩子,然后设置当前元素和左右孩子的指针关系。
// 序列化:将二叉树转换为字符串 char* Serialize(TreeNode *root) { string str; queue<TreeNode *> que; if(!root) //空 return nullptr; //根节点入队 que.push(root); //入队的同时处理字符串 以感叹号分割数值 因为一个数值可能有多位 str+=to_string(root->val)+'!'; while(!que.empty()) { TreeNode* ch=que.front(); que.pop(); if(ch->left) { que.push(ch->left); //由于不为空时会处理空字符串 所以不在出队的时候处理 而是在入队的时候处理 str+=to_string(ch->left->val)+'!'; } else str+='#'; if(ch->right) { que.push(ch->right); str+=to_string(ch->right->val)+'!'; } else str+='#'; } //最后一位‘\0’ 不占长度但占空间 char *p=new char[str.size()+1]; strcpy(p,str.c_str()); return p; } // 反序列化:将字符串转换为二叉树 TreeNode* Deserialize(char *str) { if(!str||str[0]=='#') //空 return nullptr; string res=str; vector<string> vec; //分割数值 //一个string一个数值 #表示空 for(int i=0;i<res.size();i++) { if(res[i]=='#') //空 vec.push_back("#"); else { int j=i; while(res[j]!='!') //!分割数值 j++; vec.push_back(res.substr(i,j-i)); i=j; //i=j 因为出来后i会继续加一的 } } //stoi将string转换为int 构造根节点 TreeNode *root=new TreeNode(stoi(vec[0])); queue<TreeNode *> que; que.push(root); //根节点第一个孩子 int k=1; //一共k个元素 while(k<vec.size()) { TreeNode *p=que.front(); que.pop(); //左孩子 TreeNode* l=nullptr; //右孩子 TreeNode* r=nullptr; if(vec[k]!="#") //左孩子非空 { l=new TreeNode(stoi(vec[k])); que.push(l); } //默认是nullptr p->left=l; if(vec[k+1]!="#") //右孩子非空 { r=new TreeNode(stoi(vec[k+1])); que.push(r); } //默认是nullptr p->right=r; k+=2; //指针后移两步 } return root; }