数据结构:二叉树的序列化和反序列化(序列和二叉树结构一一对应)

文章目录

  • 一、基础知识
    • 1.1 序列化和反序列基础知识
    • 1.2 string与int转化STL函数
  • 二、详解说明
    • 2.1 基于前序遍历的序列化和反序列化
    • 2.2 基于后序遍历的序列化和反序列化
    • 2.3 基于层序遍历的序列化和反序列化
  • 三、例题——652. 寻找重复的子树

三个相同题目:
297. 二叉树的序列化与反序列化
LCR 156. 序列化与反序列化二叉树
LCR 048. 二叉树的序列化与反序列化
题目要求:

  序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
  请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

一、基础知识

1.1 序列化和反序列基础知识

  1. 如果你的序列化结果中包含空指针的信息,且你只给出一种遍历顺序,也要分两种情况:
    • 如果你给出的是前序后序层序,那么你可以还原出唯一的一棵二叉树。
    • 如果你给出的是中序,那么你无法还原出唯一的一棵二叉树
  2. 如果你的序列化结果中不包含空指针的信息,且你只给出两种遍历顺序,也要分两种情况:
    • 如果你给出的是前序和中序,或者后序和中序,那么你可以还原出唯一的一棵二叉树。
    • 如果你给出前序和后序,那么你无法还原出唯一的一棵二叉树。

我们序列化二叉树时使用包含空指针信息的,这样我们只需要按照一种顺序序列化二叉树并且可以直接还原二叉树了。空指针的值我们用#表示。转换思想在详细说明里解释。

  • 这样我们使用一种遍历顺序序列化二叉树后,这个序列和二叉树就是一一对应的了。换句话说,我们将一个复杂树结构转成一个直接比对的字符串了
  • 我们可以在脑子里灌入这样的思想:二叉树和一个序列唯一对应,如果不在意二叉树的结构的问题,要判断两颗二叉树相同,实际上就是转换成判断序列是否相同即可。二叉树就是序列!

序列可以用string存储。

1.2 string与int转化STL函数

to_string

  • to_string(int):传入一个整型,转换成string类型。

stoi

  • stoi(string):传入一个string类型,转换成一个整型。

二、详解说明

本文使用的二叉树结构:

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

注意在序列化过程中:

  • 需要使用其他符号区分不同值的边界,如逗号。
    • 在实现时的逗号,可以这样划分:[123,][#,][#,],在实现时我们分模块实现就能更简单。
    123,#,#,
    
  • 反序列化求值时注意负数。
  • 前序,后序和层序实现方法大同小异。
    • 能实现反序列化且的机理实际上就是非叶节点都有左右子树,叶节点左右子树就在自己附近直接生成,它往上返回实际上就代表了该非叶结点确定了一个儿子,依次往上发现只要前面只有俩就必然可以是它左右子树了。然后实现的时候用逗号分割,实际上在反序列化的时候这里只是逗号点麻烦。

2.1 基于前序遍历的序列化和反序列化

对于一个前序序列而言:在二叉树中表示是这样的 [ 根 ] , [ 左子树 ] , [ 右子树 ] [根],[左子树],[右子树] [],[左子树],[右子树](逗号区分不同值的边界)

  • 我们很明显可以知道,当前序列的首字符是根,我们得到左子树后连接在根的左子树上,得到右子树后连接在根的右子树上。
  • 由于空指针我们用#表示,只有空指针没有儿子
  • 之后的后根和层序是差不多的。

反序列化使用递归的方法:
在这里插入图片描述

class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {if(!root) return "#,";return to_string(root->val) + "," + serialize(root->left) + serialize(root->right);//string : 根 + 左子树序列 + 右子树序列 -->(前序序列)}// Decodes your encoded data to tree.TreeNode* deserialize(string s) {data = s;return Deserialize();}
private:string data;int idx = 0;TreeNode * Deserialize(){if(data[idx] == '#') {idx += 2;return nullptr;}//#, 模块int start = idx;while(data[idx++] != ',');TreeNode * root = new TreeNode(stoi(data.substr(start,idx-start-1)));//当前序列的首字符是根root->left = Deserialize();//先生成左子树,连接上root->right = Deserialize();//然后生成右子树,连接上return root;}
};
  • 为何在实现的时候不直接使用函数deserialize
    • 因为它每次传参是拷贝的,速度很慢

2.2 基于后序遍历的序列化和反序列化

反序列化使用栈实现的方法:(前序也可以,不过不方便)
在这里插入图片描述

class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {if(!root) return "#,";return serialize(root->left) + serialize(root->right) + to_string(root->val) + ",";//string : 左子树序列 + 右子树序列 + 根  -->(后序序列)}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {stack<TreeNode *> sta;for(int i = 0;i < data.size();){//我们要注意string的最后一个是',',因此要提前退出if(data[i] == '#') {sta.push(nullptr);i += 2;}else{int start = i;while(data[i++] != ',');TreeNode * cur = new TreeNode(stoi(data.substr(start,i-start-1)));cur->right = sta.top();sta.pop();cur->left = sta.top();sta.pop();sta.push(cur);}}return sta.top();}
};

2.3 基于层序遍历的序列化和反序列化

反序列化使用队列实现的方法:
在这里插入图片描述

class Codec {
public:// Encodes a tree to a single string.string serialize(TreeNode* root) {string s = "";queue<TreeNode *> q;q.push(root);while(!q.empty()){TreeNode * cur = q.front();q.pop();if(!cur){s += "#,";}else{s += to_string(cur->val) + ",";q.push(cur->left);q.push(cur->right);}}return s;}// Decodes your encoded data to tree.TreeNode* deserialize(string data) {queue<TreeNode *> q;int i = 0;TreeNode * ans = deal(data,i);q.push(ans);for(;i < data.size();){TreeNode * root = q.front();q.pop();root->left = deal(data,i);root->right = deal(data,i);if(root->left) q.push(root->left);if(root->right) q.push(root->right);}return ans;}
private:TreeNode * deal(string & data,int & i){//生成一个结点if(data[i] == '#'){i += 2;return nullptr;}int start = i;while(data[i++] != ',');return new TreeNode(stoi(data.substr(start,i-start-1)));}
};
// 1 , 2 , 3 , # , # , # , # ,

三、例题——652. 寻找重复的子树

LeetCode:652. 寻找重复的子树

在了解序列化之后,我们知道,序列和二叉树一一对应。
因此我们寻找重复子树可以直接寻找构造的序列来判断二叉树是否有相同。

本题并不需要反序列化,毕竟只需要存储二叉树结点,root是知道的,直接存就行了。

class Solution {
public:unordered_map<string,int> st;vector<TreeNode *> ans;vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {find(root);return ans;}
private:string find(TreeNode * root){//为防止重复遍历,我们用后序遍历,这样可以减少重复。边后序遍历边构造root的串串if(!root) return "#,";//构造root的string序列!string s = find(root->left) + find(root->right) + to_string(root->val) + ",";if(st[s] == 1) ans.push_back(root);st[s]++;return s;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/707407.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pencils Protocol Season 2 收官在即,Season 3 携系列重磅权益来袭

此前Scroll生态LaunchPad &聚合收益平台Pencils Protocol&#xff08;原Penpad&#xff09;&#xff0c;推出了首个资产即其生态代币PDD的Launch&#xff0c;Season 2活动主要是用户通过质押ETH代币、组件战队等方式&#xff0c;来获得Point奖励&#xff0c;并以该Point为依…

C++那些事之Mixin惯用法

C那些事之Mixin惯用法 大家好&#xff0c;我是光城&#xff0c;今天给大家分享C那些事里面的一个惯用法&#xff1a;mixin 混合&#xff08;Mixins&#xff09;是Lisp中的一个概念。混合是类的一部分&#xff0c;意味着它旨在与其他类或混合组合在一起。常规独立类&#xff08;…

C++学习~~string类

1.STL简单介绍 &#xff08;1&#xff09;标准模版库&#xff0c;是C里面的标准库的一部分&#xff0c;C标准库里面还有其他的东西&#xff0c;但是我们不经常使用&#xff0c;我们经常使用的还是STL这个标准库部分。 &#xff08;2&#xff09;六大件&#xff1a;仿函数&…

内网安全工具之ADExplorer的使用

ADExplorer是域内一款信息查询工具&#xff0c;它是独立的可执行文件&#xff0c;无需安装。它能够列出域组织架构、用户账号、计算机账号登&#xff0c;可以帮助寻找特权用户和数据库服务器等敏感目标。 下载地址&#xff1a;http://live.sysinternals.com/ 连接 下载了ADE…

SSM宠物管理系统-计算机毕业设计源码56932

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对宠物管理系统等问题&#xff0c;对宠物管理…

使用Docker在阿里云ECS上部署Gitlab,提供代码托管、CICD 和 docker镜像服务

文章目录 使用Docker在阿里云ECS上部署Gitlab1.购买一个数据&#xff0c;挂载到/data用于存储gitlab相关数据2. 部署docker引擎3. 调整ssh的默认端口&#xff0c;将22端口留给gitlab4. 部署gitlab5. 进入docker容器获取gitlab的默认密码6. 登录gitlab&#xff0c;完成gitlab-ru…

Spring Boot代码案例(计算器、登录、留言板)

文章目录 一、计算器二、登录2.1 判断账号密码是否正确2.2 根据不同的用户作出不同反应 三、留言板3.1 提交数据3.2 展示所有数据 四、Lombok 工具包4.1 场景介绍4.2 如何使用 五、Edit Starters插件六、项目如何Debug七、项目命名规范 一、计算器 导入前端文件后端代码&#…

Risk Of Rain 雨中冒险2服务器开服联机教程

1、购买后登录服务器&#xff08;百度莱卡云&#xff09; 1.1、第一次购买服务器会安装游戏端&#xff0c;大约5分钟左右&#xff0c;如果长时间处于安装状态请联系客服 2、设置游戏端口 由于雨中冒险2的设置需要两个端口&#xff0c;它们用于游戏端口&#xff0c;查询端口&am…

理解 Python 中的 `super()` 与 `__init__()` 方法

在 Python 的面向对象编程中&#xff0c;super() 函数和 __init__() 方法是两个非常重要的概念。它们在类的继承和初始化过程中扮演着关键的角色。本文将深入探讨这两个概念的工作原理&#xff0c;并通过示例代码来展示它们的使用。 基本原理 __init__() 方法 __init__() 是…

K8S认证 | CKA题库 + 答案 | 查看Pod CPU资源使用量

2、查看集群中运行Pod CPU资源使用量 您必须在以下Cluster/Node上完成此考题&#xff1a; Cluster Master node Worker node k8s …

Vitis HLS 学习笔记--资源绑定-使用URAM(1)

目录 1. 简介 2. 代码分析 2.1 存储器代码 2.2 Implementation报告 2.3 存储器类型指定 2.4 存储器初始化 3. 总结 1. 简介 在博文《Vitis HLS 学习笔记--资源绑定-使用URAM-CSDN博客》中&#xff0c;介绍了如何在Vitis HLS环境下设计一个简易的存储器模型。 通过以下…

贝努利贝叶斯算法

基本用法 完整代码&#xff1a; from sklearn.naive_bayes import BernoulliNB from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split# 生成数据 X, y make_blobs(n_samples500, centers5, random_state8) X_train, X_test, y_tr…