剑指offer之序列化和反序列化二叉树C++解法

剑指offer之序列化和反序列化二叉树C++解法


题目描述:请实现两个函数,分别用来序列化和反序列化二叉树

序列化:二叉树被记录成文件的过程叫作二叉树的序列化
反序列化:通过文件内容重建原来的二叉树过程叫做二叉树反序列化
原文链接:https://rongxuanhong.github.io/2018/07/15/剑指offer2之序列化二叉树/

我们知道仅有前序遍历是无法确定一颗二叉树的,所以我们还需要要给叶节点的空指针赋予特殊的标记 ‘#’,这样一来,当递归遇到’#’时,我们就知道了该跳出递归了,回溯完之后就进入右子树处理了。
另外,二叉树的节点值可能有两位数,这样就不能单靠指针的移动就能确定二叉树的节点值,还需要加入分割标记,因此我们序列化的时候,需额外追加逗号,分割开不同的二叉树节点值,方便反序列化取出节点值。

方法一:vector

  1. 借助整型类型的vector数组存储二叉树节点值,所以进行序列化的时候,对二叉树进行前序遍历将节点加入数组,当节点为空的时候,加入特殊数值0xFFFFFFF,并退出递归。由于函数需要返回字符数组类型,因此还需要将vector数组中的元素一一复制到动态整型数组中,最后对整型数组强制转换为字符类型。
  2. 反序列化时,先将字符数组类型转为整型数组,然后二叉树的节点的值就可以直接从整型数组中得到。首先,处理递归出口,如果遇到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

  1. 借助string类型来拼接序列化的字符串。只需要前序遍历二叉树,当遇到根节点为空时,追加‘#’,并退出递归,否则追加二叉树的根节点值,接着追加逗号。接下来只需递归实现左子树和右子树的序列化。那么,序列化之后得到了string类型的字符串,最后构造动态字符数组,将字符串的每一个字符复制到字符数组中,结尾加入’\0’标记结束。
  2. 进行反序列化时,如果遇到’#’,那么那么指针移动一个单位,并退出递归。否则,找到字符数组中的一个二叉树节点值,然后构造根节点,如果此时到达字符末尾,直接返回根节点,否则指针继续移动一个单位。接下来根结点的左右指针分别连接左子树的递归实现结果和右子树的递归实现结果。
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);
    }
};
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树序列化反序列化是将一棵二叉树的状态转换为一个字符串,然后又恢复成原来的二叉树的过程,通常用于数据存储和在网络中传输。在C++中,我们可以使用自定义的数据结构和迭代方法来实现这个功能。 **序列化(Serialization)**: 1. 定义一个序列化的格式,常见的如前序遍历(根-左-右)或中序遍历(左-根-右)的字符串表示。 2. 创建一个函数,接受二叉树的根节点作为输入,使用递归的方式访问每个节点,并将其值和子节点信息添加到序列中。如果节点为空,可以用特殊标记(如“#”或“null”)表示。 3. 返回最终序列化的字符串。 ```cpp string serialize(TreeNode* root) { // 假设使用前序遍历 stack<TreeNode*> nodes; nodes.push(root); string result = ""; while (!nodes.empty()) { TreeNode* node = nodes.top(); nodes.pop(); if (node) { result += to_string(node->val) + ','; nodes.push(node->left); nodes.push(node->right); } else { result += "#,"; } } return result.substr(0, result.length() - 1); // 去掉末尾逗号 } ``` **反序列化(Deserialization)**: 1. 创建一个函数,接受一个字符串作为输入,恢复成二叉树。首先,从字符串中分割出节点值和子节点信息。 2. 用堆栈或队列模拟前序或中序遍历的过程,根据节点值创建新的节点,并将它们连接起来。 3. 当遇到“#”或“null”时,表示当前节点为空,停止添加子节点。 ```cpp TreeNode* deserialize(string data) { if (data == "") return nullptr; stack<TreeNode*> nodes; char* token = strtok(data.c_str(), ","); while (token != nullptr) { int val = stoi(token); if (isdigit(val)) { // 创建新节点并入栈 TreeNode* node = new TreeNode(val); nodes.push(node); if (nodes.top()->left == nullptr) { nodes.top()->left = nodes.top()->right = nullptr; // 初始化为 null } else { TreeNode* parent = nodes.top(); if (parent->left == nullptr) { parent->left = node; } else { parent->right = node; } } } else { nodes.top()->left = deserialize(next_token); nodes.top()->right = deserialize(next_token); token = strtok(nullptr, ","); } } return nodes.top(); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值