leetcode:297. 二叉树的序列化与反序列化

本文探讨了如何通过前序遍历策略实现二叉树的序列化和反序列化,涉及特殊标记null节点、层次结构编码以及广度优先搜索与深度优先搜索的应用。两种实现方式和编码/解码过程详细解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述


struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {

    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {

    }
};

题目解析

二叉树序列化的本质是对其进行编码,更重要的是对其结构进行编码。可以遍历树来完成上述任务。一般有两个策略:广度优先搜索和深度优先搜索

  • 广度优先搜索可以按照层次从上往下遍历所有节点
  • 深度优先搜索可以从一个根节点开始,一直延伸到某个叶,然后回到根,到达另一个分支。根据根节点、左节点和右节点之间的相对顺序,可以进一步将深度优先搜索策略区分为:
    • 先序遍历
    • 中序遍历
    • 后序遍历

深度优先搜索

序列化:

  • 递归遍历一棵树,重点关注当前节点,它的子树的遍历交给递归完成:“serialize函数,请帮我分别序列化我的左右子树,我等你返回的结果,再拼接一下。”
  • 选择前序遍历,是因为 根|左|右、根∣左∣右 的打印顺序,在反序列化时更容易定位出根节点的值。
  • 遇到 null 节点也要翻译成特定符号,反序列化时才知道这里是 null。

在这里插入图片描述

反序列化

  • 前序遍历的序列化字符串,就像下图右一:

在这里插入图片描述

  • 定义函数 buildTree 用于还原二叉树,传入由序列化字符串转成的 list 数组。
  • 逐个 pop 出 list 的首项,构建当前子树的根节点,顺着 list,构建顺序是根节点,左子树,右子树。
    • 如果弹出的字符为 “X”,则返回 null 节点。
    • 如果弹出的字符是数值,则创建root节点,并递归构建root的左右子树,最后返回root。
      在这里插入图片描述

总的来说

(1)怎么区分节点(节点和节点之间、节点与null节点)

  • 需要使用# 等特殊标记每一个null
  • 需要使用 , 等字符区分每一个节点(注意null也是一个节点)

(2)

  • 序列化时:前序遍历,根节点–>左节点–>右节点
  • 反序列时:前序遍历,先构建出根节点 ,然后构建左节点,再构建右节点(每构建一个节点,都需要从原始字符中移除)

实现一

class Codec  {
    void serialize(TreeNode *root, ostringstream &out) {
        if(root){
            out << root->val << ' ';
            serialize(root->left, out);
            serialize(root->right, out);
        }else{
            out << "# ";
        }
    }

    TreeNode *deserialize(istringstream &in){
        std::string val;
        in >> val;
        if(val == "#"){
            return nullptr;
        }
        TreeNode *root = new TreeNode(stoi(val));
        root->left = deserialize(in);
        root->right = deserialize(in);
        return root;
    }
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        std::ostringstream out;
        serialize(root, out);
        return out.str();
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        std::istringstream in(data);
        return deserialize(in);
    }
};

实现二

class Codec  {

    static TreeNode *buildTree( std::list<std::string> & strList){
        std::string strtmp = strList.front();
        strList.pop_front();
        if(strtmp == "X"){
            return NULL;
        }
        TreeNode *node = new TreeNode(stoi(strtmp));
        node->left = buildTree(strList);
        node->right = buildTree(strList);
        return node;
    }

    static std::list<std::string> split(std::string & data){
        int len = data.size();
        std::string curr;
        std::list<std::string> dataArray;
        for(char & ch : data){
            if(ch == ','){
                dataArray.push_back(curr);
                curr.clear();
            }else{
                curr.push_back(ch);
            }
        }
        if(!curr.empty()){
            dataArray.push_back(curr);
            curr.clear();
        }
        return dataArray;
    }
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        if(root == NULL){  // 遍历到 null 节点
            return "X";
        }
        std::string left = serialize(root->left);
        std::string right = serialize(root->right);
        return std::to_string(root->val) + "," + left + "," + right;   // 按  根,左,右  拼接字符串
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        list<string> list = split(data);
       TreeNode* res = buildTree(list);
        return res;
    }
};

在这里插入图片描述

广度优先搜索

序列化

  • 维护一个队列,初始让根节点入队,考察出队节点:
    • 如果出队的节点是NULL,将x推入res
    • 如果出队的节点是数值,将节点值推入res,并将它的左右节点入队
      - 子节点 null 也要入列,它对应 “X”,要被记录,只是它没有子节点可入列
  • 入列、出列…直到队列为空,就遍历完所有节点,res构建完毕,转成字符串就好。

反序列化

下图是BFS得到的序列化字符串,和DFS得到的不同,它是一层层的。除了第一个是根节点的值,其他节点值都是成对的,对应左右子节点。

在这里插入图片描述

  • 依然先转成list数组,用一个指针 cursor 从第二项开始扫描。
  • 起初,用list[0]构建根节点,并让根节点入列
  • 节点出列,此时 cursor 指向它的左子节点值,cursor+1 指向它的右子节点值。
    • 如果子节点值是数值,则创建节点,并认出列的父亲,同时自己也是父亲,入列。
    • 如果子节点值为 ‘X’,什么都不用做,因为出列的父亲的 left 和 right 本来就是 null
  • 可见,所有的真实节点都会在队列里走一遍,出列就带出儿子入列

在这里插入图片描述

class Codec  {
    
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        std::ostringstream out;
        std::queue<TreeNode*> q;
        if(root){
            q.push(root);
        }
        while (!q.empty()){
            TreeNode *t = q.front(); q.pop();
            if(t){
                out << t->val << ' ';
                q.push(t->left);
                q.push(t->right);
            }else{
                out <<"# ";
            }
        }
        return out.str();
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data.empty()){
            return nullptr;
        }
        std::istringstream in(data);
        std::queue<TreeNode *>q;
        std::string val;
        in >> val;
        TreeNode *res = new TreeNode(stoi(val)), *curr = res;
        q.push(curr);
        while (!q.empty()){
            TreeNode *t = q.front(); q.pop();
            if(!(in >> val)){
                break;
            }
            if(val != "#"){
                curr = new TreeNode(stoi(val));
                q.push(curr);
                t->left = curr;
            }
            if(!(in >> val)){
                break;
            }
            if(val != "#"){
                curr = new TreeNode(stoi(val));
                q.push(curr);
                t->right = curr;
            }
        }
        return res;
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值