【剑指offer-C++】JZ37:序列化二叉树

【剑指offer-C++】JZ37:序列化二叉树

    • 题目描述
    • 解题思路

题目描述

描述:请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。

二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#)。

二叉树的反序列化(Deserialize)是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,可以根据层序遍历的方案序列化,如下图:

【剑指offer-C++】JZ37:序列化二叉树_第1张图片

层序序列化(即用函数Serialize转化)如上的二叉树转为"{1,2,3,#,#,6,7}“,再能够调用反序列化(Deserialize)将”{1,2,3,#,#,6,7}"构造成如上的二叉树。

当然你也可以根据满二叉树结点位置的标号规律来序列化,还可以根据先序遍历和中序遍历的结果来序列化。不对序列化之后的字符串进行约束,所以欢迎各种奇思妙想。

数据范围:节点数 n≤100,树上每个节点的值满足 0≤val≤150。

要求:序列化和反序列化都是空间复杂度 O(n),时间复杂度 O(n)。

输入:{1,2,3,#,#,6,7}
返回值:{1,2,3,#,#,6,7}
说明:如题面图   
输入:{8,6,10,5,7,9,11}
返回值:{8,6,10,5,7,9,11}

解题思路

序列化二叉树:最直观的想法是,使用层序遍历的方式进行存储,对于某个叶子节点的空节点进行存储,同时保证不递归存储空节点对应的子节点。序列化指的是将二叉树转换为字符串;反序列化指的是将字符串转换为二叉树。序列化可以使用层序遍历,如何保证不递归存储空节点的子节点呢?那就是队列中不存储空节点,遇到空节点时只对字符串进行相应的处理,正因如此,应该是入队时处理字符串而不是出队时处理字符串;同时由于数值可能位数不止一位,故需要使用间隔符来分割数值。反序列化可以先根据间隔符分割数值,然后将根节点压入队列,使用变量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;
}

你可能感兴趣的:(剑指offer,c++,算法,数据结构)