二叉树题目:二叉树的序列化与反序列化

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 前言
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:二叉树的序列化与反序列化

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

难度

8 级

题目描述

要求

序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定序列化和反序列化算法的执行逻辑,只需要保证一个二叉树可以被序列化为一个字符串并且这个字符串可以被反序列化为原始的树结构。

示例

示例 1:

示例 1

输入: root   =   [1,2,3,null,null,4,5] \texttt{root = [1,2,3,null,null,4,5]} root = [1,2,3,null,null,4,5]
输出: [1,2,3,null,null,4,5] \texttt{[1,2,3,null,null,4,5]} [1,2,3,null,null,4,5]

示例 2:

输入: root   =   [] \texttt{root = []} root = []
输出: [] \texttt{[]} []

数据范围

  • 树中结点数目在范围 [0,   10 4 ] \texttt{[0, 10}^\texttt{4}\texttt{]} [0, 104]
  • -1000 ≤ Node.val ≤ 1000 \texttt{-1000} \le \texttt{Node.val} \le \texttt{1000} -1000Node.val1000

前言

这道题要求实现二叉树的序列化与反序列化,将二叉树序列化为一个字符串之后可以将字符串反序列化得到原始二叉树。为了能反序列化得到原始二叉树,两个不同的二叉树的序列化结果一定不同。

二叉树的序列化可以使用深度优先搜索或广度优先搜索实现,反序列化的执行逻辑由序列化的执行逻辑决定。

解法一

思路和算法

使用深度优先搜索实现二叉树的序列化时,可以存储二叉树的前序遍历的结果,包括空结点,即如果一个非空结点的某个子结点为空,则空的子结点也需要包含在序列化的结果中。序列化的结果中,非空结点使用结点值表示,空结点使用字符 ‘#’ \text{`\#'} ‘#’ 表示,每个结点之间使用逗号分隔。该序列化的方法可以确保两个不同的二叉树的序列化结果一定不同。

例如,示例 1 的二叉树的序列化结果是 “1,2,#,#,3,4,#,#,5,#,#" \text{``1,2,\#,\#,3,4,\#,\#,5,\#,\#"} “1,2,#,#,3,4,#,#,5,#,#"

反序列化时,首先将序列化结果根据逗号分隔成字符串数组,然后遍历数组并构造二叉树。构造二叉树的过程需要模拟二叉树的前序遍历的过程,使用栈存储尚未填充左右子结点的结点。

数组的首个元素为根结点值,根据数组的首个元素创建根结点,将根结点入栈。遍历数组中的其余元素,对于每个元素,执行如下操作。

  1. 如果当前元素是 ‘#’ \text{`\#'} ‘#’,则创建空结点,否则根据当前元素创建非空结点。

  2. 栈顶结点为当前结点的父结点,判断当前结点作为父结点的左子结点还是右子结点。

    • 如果父结点的左子结点为空且不为 ‘#’ \text{`\#'} ‘#’,则将当前结点作为父结点的左子结点。

    • 否则,将当前结点作为父结点的右子结点,并将父结点出栈。

  3. 如果当前结点不为空,则将当前结点入栈。

遍历结束之后,即可得到原始二叉树,返回根结点。

代码

public class Codec {
    public String serialize(TreeNode root) {
        if (root == null) {
            return "#";
        }
        StringBuffer sb = new StringBuffer();
        sb.append(root.val);
        sb.append(',');
        sb.append(serialize(root.left));
        sb.append(',');
        sb.append(serialize(root.right));
        return sb.toString();
    }

    public TreeNode deserialize(String data) {
        if ("#".equals(data)) {
            return null;
        }
        String[] arr = data.split(",");
        TreeNode root = new TreeNode(Integer.parseInt(arr[0]));
        Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
        stack.push(root);
        boolean isLeftNull = false;
        int length = arr.length;
        for (int i = 1; i < length; i++) {
            String str = arr[i];
            TreeNode parent = stack.peek();
            TreeNode node = "#".equals(str) ? null : new TreeNode(Integer.parseInt(str));
            if (parent.left == null && !isLeftNull) {
                parent.left = node;
                if (node == null) {
                    isLeftNull = true;
                }
            } else {
                parent.right = node;
                stack.pop();
                isLeftNull = false;
            }
            if (node != null) {
                stack.push(node);
            }
        }
        return root;
    }
}

复杂度分析

  • 时间复杂度:序列化和反序列化的时间复杂度都是 O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。序列化和反序列化的过程中,每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度包括栈空间和将字符串转化成的数组。

解法二

思路和算法

使用广度优先搜索实现二叉树的序列化时,可以存储二叉树的层序遍历的结果,包括空结点,即如果一个非空结点的某个子结点为空,则空的子结点也需要包含在序列化的结果中。序列化的结果中,非空结点使用结点值表示,空结点使用字符 ‘#’ \text{`\#'} ‘#’ 表示,每个结点之间使用逗号分隔。该序列化的方法可以确保两个不同的二叉树的序列化结果一定不同。

例如,示例 1 的二叉树的序列化结果是 “1,2,3,#,#,4,5,#,#,#,#" \text{``1,2,3,\#,\#,4,5,\#,\#,\#,\#"} “1,2,3,#,#,4,5,#,#,#,#"

反序列化时,首先将序列化结果根据逗号分隔成字符串数组,然后遍历数组并构造二叉树。构造二叉树的过程需要模拟二叉树的层序遍历的过程,使用队列存储尚未填充左右子结点的结点。

数组的首个元素为根结点值,根据数组的首个元素创建根结点,将根结点入队列。遍历数组中的其余元素,对于每个元素,执行如下操作。

  1. 如果当前元素是 ‘#’ \text{`\#'} ‘#’,则创建空结点,否则根据当前元素创建非空结点。

  2. 队首结点为当前结点的父结点,判断当前结点作为父结点的左子结点还是右子结点。

    • 如果父结点的左子结点为空且不为 ‘#’ \text{`\#'} ‘#’,则将当前结点作为父结点的左子结点。

    • 否则,将当前结点作为父结点的右子结点,并将父结点出队列。

  3. 如果当前结点不为空,则将当前结点入队列。

遍历结束之后,即可得到原始二叉树,返回根结点。

代码

public class Codec {
    public String serialize(TreeNode root) {
        if (root == null) {
            return "#";
        }
        StringBuffer sb = new StringBuffer();
        sb.append(root.val);
        sb.append(',');
        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            TreeNode left = node.left, right = node.right;
            if (left == null) {
                sb.append('#');
            } else {
                sb.append(left.val);
                queue.offer(left);
            }
            sb.append(',');
            if (right == null) {
                sb.append('#');
            } else {
                sb.append(right.val);
                queue.offer(right);
            }
            sb.append(',');
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    public TreeNode deserialize(String data) {
        if ("#".equals(data)) {
            return null;
        }
        String[] arr = data.split(",");
        TreeNode root = new TreeNode(Integer.parseInt(arr[0]));
        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        queue.offer(root);
        boolean isLeftNull = false;
        int length = arr.length;
        for (int i = 1; i < length; i++) {
            String str = arr[i];
            TreeNode parent = queue.peek();
            TreeNode node = "#".equals(str) ? null : new TreeNode(Integer.parseInt(str));
            if (parent.left == null && !isLeftNull) {
                parent.left = node;
                if (node == null) {
                    isLeftNull = true;
                }
            } else {
                parent.right = node;
                queue.poll();
                isLeftNull = false;
            }
            if (node != null) {
                queue.offer(node);
            }
        }
        return root;
    }
}

复杂度分析

  • 时间复杂度:序列化和反序列化的时间复杂度都是 O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。序列化和反序列化的过程中,每个结点都被访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度包括队列空间和将字符串转化成的数组。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构:堆与堆排序

目录 堆的定义&#xff1a; 堆的实现&#xff1a; 堆的元素插入&#xff1a; 堆元素删除&#xff1a; 堆初始化与销毁&#xff1a; 堆排序&#xff1a; 堆的定义&#xff1a; 堆是一种完全二叉树&#xff0c;完全二叉树定义如下&#xff1a; 一棵深度为k的有n个结点的二…

微信小程序的nodejs+vue课堂在线学习系统教学辅助平台PHP设计与实现

小程序主要实现功能&#xff1a;一、用户的登录与实现 二、课程页面。学生们可以观看课程视频【课程视频有章程】&#xff0c;搜索课程&#xff0c;课程签到&#xff0c;评论课程&#xff0c;课后答题&#xff08;课后成绩&#xff09;&#xff0c;课程互动&#xff08;在视频下…

【深度学习】手把手教你使用 Auto DL 远程服务器连接 PyCharm

前言 文章性质&#xff1a;实操记录 &#x1f4bb; 主要内容&#xff1a;主要记录了如何租用 Auto DL 服务器&#xff0c;以及如何在 PyCharm 中连接远程服务器。 相关文档&#xff1a;如何使用 Auto DL 远程服务器连接 PyCharm 运行代码 - 知乎 冷知识1&#xff1a;小伙伴们不…

c++:string相关的oj题(把字符串转换成整数、344.反转字符串、387. 字符串中的第一个唯一字符、917. 仅仅反转字母)

文章目录 1.把字符串转换成整数题目详情代码思路 2. 344.反转字符串题目详情代码1思路1代码2思路 3. 387. 字符串中的第一个唯一字符题目详情代码思路 4. 917. 仅仅反转字母题目详情代码思路 1.把字符串转换成整数 传送门 题目详情 代码 class Solution { public:int StrToI…

提升用户体验的利器——TTS语音合成软件盘点

提升用户体验的利器——TTS语音合成软件盘点 在当今信息爆炸的时代&#xff0c;人们每天都要处理大量的文本信息。因此&#xff0c;将文本信息转化为语音信息&#xff0c;使得信息能够以更自然、更方便的方式传达给人们&#xff0c;就显得尤为重要。这就是TTS&#xff08;Text…

【C++修行之道】竞赛常用库函数(sort,min和max函数,min_element和max_element、nth_element)

目录 一、sort 1.1sort简介 语法 参数 功能 适用容器 1.2sort的用法 1.3自定义比较函数 示例 1265蓝桥题 —— 排序 二、min和max函数 三、min_element和max_element 497蓝桥题 —— 成绩分析 四、nth_element 一、sort 1.1sort简介 sort函数包含在头文件<a…

手机软件的测试主要有哪些方面去测试,性能测试用什么去测试好?

手机App软件与Web软件系统的架构是不一样的&#xff0c;手机是基于CS架构&#xff0c;而Web系统是基于BS架构的&#xff0c;所以测试手机App软件那么要考虑的东西会更多一些。 分析题主的问题包含两块&#xff1a; 1、手机软件(App)测试主要有哪些方面&#xff1f; 2、手机软件…

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

java数据结构与算法刷题-----LeetCode645. 错误的集合(位运算解法需要重点掌握)

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 法一&#xff1a;桶排序思想法二&#xff1a;位运算 法一&#x…

gdip-yolo项目解读:gdip模块 |mdgip模块 |GDIP regularizer模块的使用分析

gdip-yolo是2022年提出了一个端到端的图像自适应目标检测框架&#xff0c;其论文中的效果展示了良好的图像增强效果。其提出了gdip模块 |mdgip模块 |GDIP regularizer模块等模块&#xff0c;并表明这是效果提升的关键。为此对gdip-yolo的项目进行深入分析。 gdip-yolo的论文可以…

ARM 驱动 1.22

linux内核等待队列wait_queue_head_t 头文件 include <linux/wait.h> 定义并初始化 wait_queue_head_t r_wait; init_waitqueue_head(&cm_dev->r_wait); wait_queue_head_t 表示等待队列头&#xff0c;等待队列wait时&#xff0c;会导致进程或线程被休眠&…

springsecurity集成kaptcha功能

前端代码 本次采用简单的html静态页面作为演示&#xff0c;也可结合vue前后端分离开发&#xff0c;复制就可运行测试 项目目录 登录界面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</…

详谈c++智能指针!!!

文章目录 前言一、智能指针的发展历史1.C 98/03 的尝试——std::auto_ptr2.std::unique_ptr3.std::shared_ptr4.std::weak_ptr5.智能指针的大小6.智能指针使用注意事项 二、智能指针的模拟实现三、C11和boost中智能指针的关系 前言 C/C 语言最为人所诟病的特性之一就是存在内存…

Quartus II使用小技巧

工程结构&#xff1a; 在建立完某项设计的文件后&#xff0c;依次在其里面新建四个文件夹&#xff0c;分别为&#xff1a;rtl、qprj、msim、doc。 rtl文件夹用于存放设计的源文件。 doc文件夹用于存放设计的一些文档性的资料。 qprj文件夹用于存放quaruts 工程以及quartus生…

陪玩系统:最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码

首发价值29800元的最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码 &#xff08;价值29800&#xff09;最新陪玩3.0独立版本 &#xff0c;文件截图 结尾将会附上此系统源码以及详细搭建教程包含素材图仅用于学习使用 陪玩系统3.0独立升级版正式发布&#xff0c;此版本…

项目管理中如何有效沟通?项目管理有效沟通指南

无论是少数人的小型企业还是拥有数十名员工的大公司&#xff0c;有效的沟通对于确保每个人都参与并准备好在项目中实现相同的目标至关重要。 然而&#xff0c;由于沟通不畅&#xff0c;似乎在翻译中总是丢失一些东西。事实上&#xff0c;根据布兰迪斯大学的一项研究&#xff0c…

【复现】SpringBlade SQL 注入漏洞_22

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 SpringBlade 是由一个商业级项目升级优化而来的SpringCloud微服务架构&#xff0c;采用Java8 API重构了业务代码&#xff0c;完全…

一文梳理Windows自启动位置

不同版本的Windows开机自启动的位置略有出入&#xff0c;一般来说&#xff0c;Windows自启动的位置有&#xff1a;自启动文件夹、注册表子键、自动批处理文件、系统配置文件等。如果计算机感染了木马&#xff0c;很有可能就潜伏于其中&#xff01;本文将说明这些常见的Windows开…

GitHub README-Template.md - README.md 模板

GitHub README-Template.md - README.md 模板 1. README-Template.md 预览模式2. README-Template.md 编辑模式References A template to make good README.md. https://gist.github.com/PurpleBooth/109311bb0361f32d87a2 1. README-Template.md 预览模式 2. README-Templat…

CHS_02.2.2.2+调度的目标 调度算法的评价指标

CHS_02.2.2.2调度的目标 调度算法的评价指标 知识总览CPU利用率系统吞吐量周转时间等待时间响应时间 知识回顾 在这个小节中 我们会学习一系列用于评价一个调度算法好坏的一些评价指标 知识总览 包括cpu利用率 系统吞吐量 周转时间 等待时间和响应时间 那在学习的过程中 要注意…