LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九

这篇具有很好参考价值的文章主要介绍了LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇概览

  • 因为欣宸个人水平有限,在刷题时一直不敢面对hard级别的题目,生怕出现一杯茶一包烟,一道hard做一天的窘境
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九
  • 这种恐惧心理一直在,直到遇见了它:LeetCode297,建议不敢做hard题的新手们速来围观,拿它练手,轻松找到自信

题目简介

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

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

提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
  • 提示
提示:

树中结点数在范围 [0, 104] 内
-1000 <= Node.val <= 1000
  • 接下来,先开始轻松愉快的分析工作

分析

  • 小结一下题目要求,需要做两件事:
  1. serialize方法:输入二叉树根节点,返回字符串
  2. deserialize方法:输入字符串,方法内部根据字符串构建一棵二叉树,然后返回根节点
  • 说实话,当时读题后的第一反应是:这是二叉树的基本操作嘛,一定是个easy,结果发现官方设定的难度是hard,当时就觉得赚大了!!!
  • 简单的说,解题思路只有四个字:前序遍历
  • 前序遍历是啥?很简单,是指一种遍历二叉树的顺序,看着下面的图,咱们一起默念:根左右,所以前序遍历下图二叉树的结果是:1,2,3
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九
  • 类似的还有中序遍历,中序遍历要求根节点在输出位置的中间,也就是左根右,还是上面那个二叉树,中序遍历的结果是:2,1,3
  • 还有后续遍历是左右根,上面那个二叉树,后序遍历的结果是:2,3,1
  • 至于本题为何要选前序遍历,因为字符串转二叉树时,会涉及到数组,而将根节点放在数组的最前面,这样既便于处理也便于理解
  • 再来看看前序遍历的代码一般怎么写?或者说套路是什么?
  • 伪代码如下,可见是个非常简单的递归操作
private void dfs(TreeNode root) {
  // 终止条件是发现入参为空
  if(null==root) {
    return;
  }
  
  // 1. 根
  处理root的代码
  // 2. 左
  dfs(root.left);
  // 3. 右
  dfs(root.right);
}
  • 以前面那个图上的二叉树为例,分析上述代码如何执行:调用dfs的时候传入的是根节点,在dfs方法中,处理完根节点后,立即调用dfs处理左节点,在处理左节点的时候还会再递归一次,不让过左节点的子节点都是null,所以这个递归啥事也没做,处理完左节点后再调用dfs处理右节点,这样就完成了根左右的处理
  • 没错,二叉树遍历的套路就是这么简单,至于中序和后续遍历的代码,和上面的差不多,无非是将三段代码的调用顺序调整一下即可
  • 接下来,编码

编码:序列化

  • 先看序列化的代码
  • 首先准备一个StringBuilder类型的成员变量serializeRes,遍历到的每一个元素都存放在serializeRes的尾部,用逗号分隔
private StringBuilder serializeRes;
  • 然后是serialize方法的实现,首先要判断root为空的特殊情况,另外就是serializeRes的初始化,然后就会调用serializeDfs方法,这个serializeDfs就是遍历二叉树的具体实现
public String serialize(TreeNode root) {
        if (null==root) {
            return null;
        }

        serializeRes = new StringBuilder();
        serializeDfs(root);
        return serializeRes.toString();
    }
  • 遍历二叉树的核心逻辑,serializeDfs方法如下,可见和咱们刚才的伪代码很像,每一个节点都会被存入serializeRes,并且以逗号分隔,只有一处需要注意,就是每当遇到root等于null,就在serializeRes尾部追加一个n,这样在serializeRes中就相当于每个节点没有左子节点或者右子节点的标志,这很重要!
private void serializeDfs(TreeNode root) {
  if(null==root) {
    serializeRes.append("n,");
    return;
  }

  // 1. 根
  serializeRes.append(root.val).append(",");
  // 2. 左
  serializeDfs(root.left);
  // 3. 右
  serializeDfs(root.right);
}
  • 以前面图中那个最简单的二叉树为例是,上述代码输出的字符串内容如下,3在2,n,n之后,显然是将2和2的子节点都处理完毕后,才去处理3,这就是典型的根左右
1,2,n,n,3,n,n,

编码:反序列化

  • 接下来的反序列化,也是严格准守根左右的顺序实现的,关键是注意对字符n的处理,它表示一个节点没有左子节点或者右子节点了
  • 首先是两个环境变量,deserializeArray是个数组,字符串用逗号分割之后生成的数组,代表整个要恢复的二叉树的所有元素,deserializeOffset用于记录数组中已经有多少个元素已经被回复到二叉树中了:
  private String[] deserializeArray;

  private int deserializeOffset;
  • 然后是最外层的发序列化方法,可见首先是处理异常逻辑,然后就会用字符串分割生成数组,再调用构建二叉树的核心逻辑deserializeDfs:
    public TreeNode deserialize(String data) {
        if (null==data) {
            return null;
        }

        deserializeArray = data.split(",");
        deserializeOffset = 0;
        return deserializeDfs();
    }
  • 最后看构建二叉树的核心逻辑deserializeDfs方法,不要以为构建二叉树的代码会比遍历二叉树的代码复杂,仔细看,发现还是严格准守根左右的顺序去处理的,先生成根节点,然后递归生产左子树和右子树,要注意的地方就是遇到字符n的时候就不要继续递归了,深度上已经到底了,需要返回上层,完成上层左子节点和右子节点的创建:
private TreeNode deserializeDfs() {
        if (deserializeOffset>=deserializeArray.length) {
            return null;
        }

        if ("n".equals(deserializeArray[deserializeOffset])) {
            deserializeOffset++;
            return null;
        }

        // 1. 根
        TreeNode treeNode = new TreeNode(Integer.valueOf(deserializeArray[deserializeOffset++]));
        // 2. 左
        treeNode.left = deserializeDfs();
        // 3. 右
        treeNode.right = deserializeDfs();

        return treeNode;
    }
  • 最后贴出完整的代码
public class Codec {

    // 本题的整体思路是前序遍历,即:根左右
    private StringBuilder serializeRes;

    private String[] deserializeArray;

    private int deserializeOffset;

    private void serializeDfs(TreeNode root) {
        if(null==root) {
            serializeRes.append("n,");
            return;
        }

        // 1. 根
        serializeRes.append(root.val).append(",");
        // 2. 左
        serializeDfs(root.left);
        // 3. 右
        serializeDfs(root.right);
    }


    private TreeNode deserializeDfs() {
        if (deserializeOffset>=deserializeArray.length) {
            return null;
        }

        if ("n".equals(deserializeArray[deserializeOffset])) {
            deserializeOffset++;
            return null;
        }

        // 1. 根
        TreeNode treeNode = new TreeNode(Integer.valueOf(deserializeArray[deserializeOffset++]));
        // 2. 左
        treeNode.left = deserializeDfs();
        // 3. 右
        treeNode.right = deserializeDfs();

        return treeNode;
    }


    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if (null==root) {
            return null;
        }
        
        serializeRes = new StringBuilder();
        serializeDfs(root);
        return serializeRes.toString();
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if (null==data) {
            return null;
        }
        
        deserializeArray = data.split(",");
        deserializeOffset = 0;
        return deserializeDfs();
    }
}
  • 提交代码,如下图,顺利AC,速度超97%,同时内存超93%,感觉美滋滋的,这可个是一道hard呀!
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九

小幅度优化

  • 回顾此题,似乎还有一丁点优化空间:在序列化的时候,咱们用字符n作为子节点为空的标志,例如
1,2,n,n,3,n,n,
  • 如果用空字符串取代n,那岂不是省掉了一些空间?
  • 说干就干,一共有两处,第一处在序列化的时候,用n做结束标志的那段代码,改动如下图
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九
  • 第二处是反序列化的时候,判断是否为n的那段代码,改动如下图
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九
  • 改完提交代码,效果如下图,速度和内存都有小幅度优化,第一次距离双百这么近!
    LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九
  • 至此,297的分析和实战已经完成,hard题能如此简单,实属不易遇到,所以不要错误哦,希望本文能给您一些思路,助您用最基础的代码,跑出最耀眼的成绩

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...文章来源地址https://www.toymoban.com/news/detail-701989.html

到了这里,关于LeetCode297:hard级别中最简单的存在,java版,用时击败98%,内存击败百分之九十九的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [leetcode~数位动态规划] 2719. 统计整数数目 hard

    给你两个数字字符串 num1 和 num2 ,以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件,我们称它是一个好整数: num1 = x = num2 min_sum = digit_sum(x) = max_sum. 请你返回好整数的数目。答案可能很大,请返回答案对 109 + 7 取余后的结果。 注意,digit_sum(x) 表示 x 各位数字之

    2024年01月18日
    浏览(32)
  • Spring中最简单的过滤器和监听器

            Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息

    2024年02月14日
    浏览(27)
  • 如何在十亿级别用户中检查用户名是否存在?

    不知道大家有没有留意过,在使用一些app注册的时候,提示你用户名已经被占用了,需要更换一个,这是如何实现的呢?你可能想这不是很简单吗,去数据库里查一下有没有不就行了吗,那么假如用户数量很多,达到数亿级别呢,这又该如何是好? 数据库方案 第一种方案就

    2024年02月08日
    浏览(35)
  • 【Leetcode】4. 寻找两个正序数组的中位数(Hard)

    给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2 。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (m+n)) 。 示例1: 示例2: 提示: nums1.length == m nums2.length == n 0 = m = 1000 0 = n = 1000 1 = m + n = 2000

    2024年02月09日
    浏览(37)
  • Unity | Shader基础知识(第一集:unity中最简单的shader)

    目录 一、unity的shader 二、创建一个shader(在创建时,选前三种都可以) 三、内容解读 1.shader一直都在 2.我们写shader在写什么 四、没有被干预的shader(最简单的shader) 相关阅读 编写着色器概述 - Unity 手册 一、unity的shader unity写的shader并不是真正意义上的shader。 官方解释:

    2024年02月04日
    浏览(42)
  • .net中最简单的http请求调用(比如调用chatgpt的openAI接口)

    支持.Net Core(2.0及以上)/.Net Framework(4.5及以上),可以部署在Docker, Windows, Linux, Mac。 http请求调用是开发中经常会用到的功能,因为,很多第三方功能接口往往是通过http地址的形式提供的,比如:ChatGpt、OpenAI、短信服务、在线翻译、地图服务、语音智能、等…   .net中调用http请

    2024年02月02日
    浏览(70)
  • 【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现

    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,博主最近一直在钻研动态规划算法,最近在Leetcode上刷题的时候遇到一个Hard难度的动态规划题,今天就借此机会来给大家分享一下我对这个题目的一些看法和解题思路(放心,

    2024年02月05日
    浏览(33)
  • 【算法】--- 几分钟了解直接选择排序(排序中最简单的排序)+快排(解决一切的优质算法)(中)

    👧个人主页:@小沈熬夜秃头中୧⍤⃝❅ 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:算法 🔑本章内容:选择排序中的 直接选择排序 和交换排序中的 快速排序 送给各位💌:你被黑暗敲打恰恰说明你是光明本身 记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~ 提示:

    2024年02月08日
    浏览(33)
  • 297.【华为OD机试】拼接url(字符串处理—Java&Python&C++&JS实现)

    🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(JavaPythonC++JS分别实现),详细代码讲解,助你深入学习,深度掌握!

    2024年04月12日
    浏览(36)
  • Java leetcode简单刷题记录1

    最大匹配字符串:https://leetcode.cn/problems/find-maximum-number-of-string-pairs/description/ 判断字符串数组中 字符串与某个字符串反转后是否一致; StringBuffer 或者 StringBuidler的 reverse方法 回文数: https://leetcode.cn/problems/palindrome-number/ StringBuffer 或者 StringBuidler的 reverse方法 罗马数字转整数

    2024年01月18日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包