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

news2024/11/16 3:52:22

文章目录

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

题目

标题和出处

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

出处: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 是二叉树的结点数。空间复杂度包括队列空间和将字符串转化成的数组。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据开发之Scala

第 1 章&#xff1a;scala入门 1.1 概述 scala将面向对象和函数式编程结合成一种简洁的高级语言 特点 1、scala和java一样属于jvm语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且scala能够直接调用java的类库 2、scala支持两种编程范式面向对象和函数式…

MySQL索引优化:深入理解索引下推原理与实践

随着MySQL的不断发展和升级&#xff0c;每个版本都为数据库性能和查询优化带来了新的特性。在MySQL 5.6中&#xff0c;引入了一个重要的优化特性——索引下推&#xff08;Index Condition Pushdown&#xff0c;简称ICP&#xff09;。ICP能够在某些查询场景下显著提高查询性能&a…

JVM系列-3.类的生命周期

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

想找一个没有中间商的账户,很简单,昂首资本推给你

各位投资者都知道&#xff0c;交易的成本决定我们是否盈利&#xff0c;那么有没有一个没有中间商的账户呢&#xff1f;当然有了&#xff0c;昂首资本这就推给你。 在交易的时候&#xff0c;银行为投资者提供兑换业务&#xff0c;并从中收取费用。至于经纪商&#xff0c;它是交…

爬虫requests+综合练习详解

Day2 - 1.requests第一血_哔哩哔哩_bilibili requests作用&#xff1a;模拟浏览器发请求 requests流程&#xff1a;指定url -> 发起请求 -> 获取响应数据 -> 持续化存储 爬取搜狗首页的页面数据 import requests# 指定url url https://sogou.com # 发起请求 resp…

GPS位置虚拟软件 AnyGo mac激活版

AnyGo for Mac是一款一键将iPhone的GPS位置更改为任何位置的强大软件&#xff01;使用AnyGo在其iOS或Android设备上改变其GPS位置&#xff0c;并在任何想要的地方显示自己的位置。这对那些需要测试应用程序、游戏或其他依赖于地理位置信息的应用程序的开发人员来说非常有用&…

idea远程服务调试

1. 配置idea远程服务调试 这里以 idea 新 ui 为例&#xff0c;首先点击上面的 debug 旁边的三个小圆点&#xff0c;然后在弹出的框框中选择 “Edit”&#xff0c;如下图所示。 然后进入到打开的界面后&#xff0c;点击左上角的 “” 进行添加&#xff0c;找到 “Remote JVM De…

Java 面向对象 04 构造方法(黑马)

这是以前没有赋值的时候&#xff0c;在&#xff08;&#xff09;里面是空的&#xff1a; 代码&#xff1a; 左边的是调用的空参构造&#xff0c;但是右边没有写空参构造&#xff0c;并不会报错&#xff0c;因为虚拟机会自己给外面一个空参构造的方法&#xff0c;就是这样的&am…

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

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

《SPSS统计学基础与实证研究应用精解》视频讲解:SPSS数据文件读取

《SPSS统计学基础与实证研究应用精解》4.3 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解4.3节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。本书旨在手把手教会使…

码农维权——案例分析之违法解除劳动合同(一)

目录 一、背景 二、举证责任方&#xff1a;需要公司举证 三、员工可以自证没有严重违反公司规章制度吗&#xff1f; 四、公司解除劳动合同的程序合法吗&#xff1f; 五、写在最后 一、背景 当前互联网行业普遍以”变相裁员“为目的&#xff0c;公司采用各种手段”逼迫“员…

<信息安全>《1 国内主要企业网络安全公司概览(一)》

1 深信服科技股份有限公司 信息内容LOGO成立日期2000年12月25日成立。总部深圳市南山区学苑大道1001号南山智园A1栋是否上市深信服[300454]A股市值265亿主要产品企业级网络安全云计算IT基础设施数据通信物联网员工规模9000人分支机构全球50多个荣誉国家级高新技术企业、中国软…

Python基础第七篇(Python的文件操作)

文章目录 一、文件编码二、文件的读取操作1.操作代码2.读出结果 三、文件的写出操作1.源代码2.读出结果 四、文件的追加操作1.源代码2.读出结果 这篇文章旨在深入浅出地介绍Python在文件操作上的能力&#xff0c;包括文件的编码、读取和写入等基本操作。内容丰富、易于理解&…

Macos flatter(用于快速LLL)本地编译安装(解决安装过程各种疑难杂症)

flatter是一个开源项目&#xff0c;能大大提高LLL的速度&#xff0c;项目提供的安装文档适用于Ubuntu&#xff0c;但是在macos上安装&#xff0c;总会遇到各种各样的问题&#xff0c;这里记录下所踩坑&#xff0c;帮助大家快速在macos上安装flatter。 文章目录 1.安装依赖库&am…

Unity学习-逐帧图集动画制作

首先在文件部分创建一个Sprite Library Asset 然后点击创建出来的文件 点下面的加号添加对应的图 添加完成之后点一下Apply 然后新建一个物体 添加这三个组件 其中SpriteLibrary里面 把你刚刚创建的图集文件拉过来 Sprite Resolver选择对应的动作和图片 然后开始制作动画 An…

Vue前端环境搭建以及项目搭建

安装node.js 安装node.js主要是为了安装npm工具&#xff0c;用于管理js包等&#xff0c;类似于java的maven。 去官网下载安装。 配置新的镜像源 npm config set registry https://registry.npmmirror.com安装webpack webpack是前端项目打包工具。 命令&#xff1a; npm…

5. 函数调用过程汇编分析

函数调用约定 __cdecl 调用方式 __stdcall 调用方式 __fastcall 调用方式 函数调用栈帧分析 补充说明 不同的编译器实现不一样&#xff0c;上述情况只是VC6.0的编译实现即便是在同一个编译器&#xff0c;开启优化和关闭优化也不一样即便是同一个编译器同一种模式&#xff0c;3…

基于SpringBoot的在线问卷调查管理系统

基于SpringBoot的在线问卷调查管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台主页 问卷列表 问卷详情 管理员界面 摘要 基于Spring Boot的在线问卷调…

数据结构·顺序表应用

本节应用是要用顺序表实现一个通讯录&#xff0c;收录联系人的姓名、性别、电话号码、住址、年龄 ​​​​​​​ 顺序表的实现在上一节中已经完成了&#xff0c;本节的任务其实就是应用上节写出来的代码的那些接口函数功能&#xff0c;做出来一个好看的&#xff0c;可…

判断线程/任务是否全部执行完成

判断线程/任务是否全部执行完成 需求 从网络上下载多个文件&#xff08;可能会有很多&#xff09;。最后将所有文件打包为一个压缩包。 思路 考虑可能有很多文件&#xff0c;所以采用多线程&#xff0c;一个线程去下载一个资源。最后再将所有文件夹进行打包。 问题 打包后发现…