Leetcode 剑指 Offer II 055. 二叉搜索树迭代器

news2024/11/17 13:28:31

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

实现一个二叉搜索树迭代器类 BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
  • int next()将指针向右移动,然后返回指针处的数字。
  • 注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

示例:

  • 输入
    • inputs = [“BSTIterator”, “next”, “next”, “hasNext”, “next”, “hasNext”, “next”, “hasNext”, “next”, “hasNext”]
    • inputs = [[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
  • 输出
    • [null, 3, 7, true, 9, true, 15, true, 20, false]
  • 解释
    • BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
    • bSTIterator.next(); // 返回 3
    • bSTIterator.next(); // 返回 7
    • bSTIterator.hasNext(); // 返回 True
    • bSTIterator.next(); // 返回 9
    • bSTIterator.hasNext(); // 返回 True
    • bSTIterator.next(); // 返回 15
    • bSTIterator.hasNext(); // 返回 True
    • bSTIterator.next(); // 返回 20
    • bSTIterator.hasNext(); // 返回 False

提示:

  • 树中节点的数目在范围 [1, 10^5] 内
  • 0 <= Node.val <= 10^6
  • 最多调用 10^5 次 hasNext 和 next 操作

进阶:

  • 你可以设计一个满足下述条件的解决方案吗?next() 和 hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。

题目思考

  1. 如何利用二叉搜索树的性质?
  2. 如何尽可能优化 next() 和 hasNext() 操作的时间复杂度?

解决方案

思路
  • 分析题目, 一个很容易想到的思路就是中序遍历, 将二叉搜索树转换成一个有序列表, 存储递增的值
  • 然后维护一个当前下标, 每次 next() 操作返回对应的值, 并把下标右移一位即可
  • 不过这样虽说 next() 和 hasNext() 操作的时间复杂度都是 O(1), 不过需要额外使用 O(N)的内存, 不满足进阶要求, 如何优化呢?
  • 如果我们可以直接对原树进行改造, 将其转换成一颗递增顺序树 (根节点最小, 每个节点的右子节点指向大于它的最小节点), 并维护下个节点 nex, 这样每次 next()操作只需要返回 nex 的值, 并将 nex 设为其右子节点即可
  • 这个改造过程正是前面刚做过的题目Leetcode 剑指 Offer II 052. 递增顺序搜索树, 为了方便起见, 我把对应的思路也贴在这里了:
    • 回顾二叉搜索树的性质, 其中序遍历的节点本身就是有序的, 所以如果我们保存了前一个节点, 那么在遍历当前节点时, 只需要将前一个节点的右子节点指向它即可, 然后将前一节点更新为当前节点, 以此类推, 这样最终中序遍历完成时, 就得到了题目要求的递增顺序搜索树
    • 当然, 我们在遍历到当前节点时, 也需要将其左子节点置为空, 这里之所以将当前节点而不是前一节点的左子节点置为空, 是因为这样才能保证最终所有节点的左子节点都是空, 否则最后一个节点的左子节点就可能不是空, 会导致出现循环
    • 利用上述做法, 我们就无需引入额外的列表存储, 也不需要再次遍历了
    • 另外在遍历第一个节点时, 由于它没有前一节点, 所以我们可以额外引入一个哨兵节点, 并将最开始的前一节点初始化为哨兵, 这样哨兵的右子节点就是最终形成的树的根, 返回它即可
  • 这样我们就只需要在初始化时, 使用 O(h)的空间来改造树, 之后的 next() 和 hasNext() 操作都只需要 O(1)的时间复杂度
  • 下面代码中有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O(N)/O(1): 初始化时只需要遍历每个节点一次, 为 O(N); next() 和 hasNext() 操作只需要常数时间, 为 O(1)
  • 空间复杂度 O(H): 初始化改造树时的递归调用最多使用 O(H) 栈空间, H 是树的高度
代码
class BSTIterator:
    def __init__(self, root: TreeNode):
        dummy = TreeNode(0)
        pre = dummy

        def inorder(node):
            # 中序遍历
            nonlocal pre
            if not node:
                return
            inorder(node.left)
            # 将当前节点的左子节点置为空, 注意不能将pre的左子节点置为空, 否则会漏掉最后一个节点
            node.left = None
            # 将前一节点的右子节点指向当前节点
            pre.right = node
            # 更新前一节点为当前节点
            pre = node
            inorder(node.right)

        inorder(root)
        self.nex = dummy.right

    def next(self) -> int:
        res = self.nex.val
        # 移动self.nex到中序遍历的下一个节点
        self.nex = self.nex.right
        return res

    def hasNext(self) -> bool:
        return self.nex != None

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

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

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

相关文章

华为云obs在java中的使用

1、申请obs服务。 申请完成后&#xff0c;会获得以下几个配置信息&#xff1a; AK"****************************"; SK"******************************************************"; ENDPOINT"obs.*************************"; BUCKET_NAME&q…

麻吉POS集成:如何无代码开发实现电商平台和CRM系统的高效连接

麻吉POS集成的前沿技术&#xff1a;无代码开发 在竞争激烈的电商市场中&#xff0c;商家们急需一种高效且易于操作的技术手段来实现系统间的快速连接与集成。麻吉POS以其前沿的无代码开发技术&#xff0c;让这一需求成为可能。无代码开发是一种允许用户通过图形用户界面进行编…

STDP突触设计(一)

最近看了很多的STDP的工作原理&#xff0c;比如 SNN系列&#xff5c;学习算法篇(6)脉冲时序依赖可塑性STDP_脉冲时间依赖可塑性-CSDN博客 Spike-Timing Dependent Plasticity - Scholarpedia 这两篇的介绍的代码非常的详细&#xff0c;我就不再赘述了 我设计的STDP的是基于…

了解 ignore_above 参数对 Elasticsearch 中磁盘使用的影响

在 Elasticsearch 中&#xff0c;ignore_above 参数允许你忽略&#xff08;而不是索引&#xff09;长于指定长度的字符串。 这对于限制字段的大小以避免性能问题很有用。 在本文中&#xff0c;我们将探讨 “ignore_above” 参数如何影响 Elasticsearch 中字段的大小&#xff0c…

力扣225-用队列实现栈

文章目录 力扣225-用队列实现栈示例代码实现总结收获 力扣225-用队列实现栈 示例 代码实现 class MyStack {Queue<Integer>queue1;Queue<Integer>queue2;public MyStack() {queue1new LinkedList<Integer>();queue2new LinkedList<Integer>();}public…

【C/PTA —— 13.指针2(课内实践)】

C/PTA —— 13.指针2&#xff08;课内实践&#xff09; 一.函数题6-1使用函数实现字符串部分复制6-2 拆分实数的整数部分和小数部分6-3 存在感 二.编程题7-1 单词反转 一.函数题 6-1使用函数实现字符串部分复制 void strmcpy(char* t, int m, char* s) {int len 0;char* ret …

基于瑞芯微rk3588+寒武纪 | 38TOPS INT8算力的AI边缘计算盒子,智能安防、智慧工地、智慧城管、智慧油站

边缘计算盒子 瑞芯微rk3588寒武纪 | 38TOPS INT8算力 ● 采用 Big-Little 大小核架构&#xff0c;搭载四核 A76四核 A55&#xff0c;CPU主频高达 2.4GHz &#xff0c;提供1MB L2 Cache 和 3MB L3 &#xff0c;Cache提供更强的 CPU 运算能力。 ● 高性能四核 Mali-G610 GPU&a…

Linux常用命令——awk命令

在线Linux命令查询工具 awk 文本和数据进行处理的编程语言 补充说明 awk是一种编程语言&#xff0c;用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件&#xff0c;或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能…

leetcode刷题详解—— 环形子数组的最大和

1. 题目链接&#xff1a;918. 环形子数组的最大和 2. 题目描述&#xff1a; 给定一个长度为 n 的环形整数数组 nums &#xff0c;返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上&#xff0c; nums[i] 的下一个元素是 nums[(…

蓝桥杯第198题 人物相关性分析 C++ 模拟 字符串 双指针

题目 思路和解题方法 程序首先定义了一个函数check&#xff0c;用于判断一个字符是否为字母。接下来&#xff0c;程序读取输入的整数k和一行字符串str。定义了两个空的向量a和b&#xff0c;用于存储满足条件的子串的起始位置。使用for循环遍历字符串str的每个字符&#xff0c;检…

数据结构:带头双向循环链表的实现

引言 单链表存在缺陷&#xff1a;需要从头开始找前一个节点 解决方法&#xff1a;双向链表 链表的结构&#xff08;8种&#xff09;&#xff1a; 1. 单向&#xff0c;双向 2. 带头、不带头 带头即为带哨兵位的头节点&#xff0c;第一个节点不存储有效数据。带头节点&#…

计算机网络——数据链路层-封装成帧(帧定界、透明传输-字节填充,比特填充、MTU)

目录 介绍 帧定界 PPP帧 以太网帧 透明传输 字节填充&#xff08;字符填充&#xff09; 比特填充 比特填充习题 MTU 介绍 所谓封装成帧&#xff0c;就是指数据链路层给上层交付下来的协议数据单元添加帧头和帧尾&#xff0c;使之成为帧。 例如下图所示&#xff1a; …

SQL注入漏洞的检测及防御方法

SQL注入&#xff08;SQL Injection&#xff09;是一种广泛存在于Web应用程序中的严重安全漏洞&#xff0c;它允许攻击者在不得到授权的情况下访问、修改或删除数据库中的数据。这是一种常见的攻击方式&#xff0c;因此数据库开发者、Web开发者和安全专业人员需要了解它&#xf…

主动学习入门Week1

主动学习&#xff08;Active Learning&#xff09; 介绍实例详解模型分类基本查询策略经典方法应用方向引用 介绍 主动学习是一种通过主动选择最有价值的样本进行标注的机器学习或人工智能方法。其目的是使用尽可能少的、高质量的样本标注使模型达到尽可能好的性能。也就是说&…

Docker容器间网络共享

Docker容器间网络共享 1、新建网络2、容器绑定网卡3、验证 Docker环境中为了一套应用部署多个环境、并且不修改配置文件的情况下&#xff0c;做到一键部署。要求不同容器直接的网络交互&#xff0c;使用容器名称。 网络相关常用命令 #查看网络内部信息docker network inspect b…

scrapy的建模及管道的使用

一、数据建模 通常在做项目的过程中&#xff0c;在items.py中进行数据建模 为什么建模 定义item即提前规划好哪些字段需要抓&#xff0c;防止手误&#xff0c;因为定义好之后&#xff0c;在运行过程中&#xff0c;系统会自动检查&#xff0c;配合注释一起可以清晰的知道要抓…

熬夜会秃头——beta冲刺Day4

这个作业属于哪个课程2301-计算机学院-软件工程社区-CSDN社区云这个作业要求在哪里团队作业—beta冲刺事后诸葛亮-CSDN社区这个作业的目标记录beta冲刺Day4团队名称熬夜会秃头团队置顶集合随笔链接熬夜会秃头——Beta冲刺置顶随笔-CSDN社区 一、团队成员会议总结 1、成员工作进…

scrapy框架

scrapy文档 文档链接 安装指南 — Scrapy 2.5.0 文档 (osgeo.cn)https://www.osgeo.cn/scrapy/intro/install.html 创建scrapy框架 0.介绍&#xff1a; scrapy是异步非阻塞框架 异步&#xff1a;一个主线程有20个任务&#xff0c;可以来回切换 非阻塞&#xff1a;运行的程序不需…

vue项目引入外部vue项目页面

vue项目使用iframe引入外部项目页面 vue项目引入外部项目页面有好几种方法&#xff0c;我这种使用的是iframe 示例 <template><div><iframe:src"userPageUrl"width"100%"height"800"frameborder"0"allowfullscreen&…

导入seaborn的数据集方法load_datasets的问题

sns.load_dataset使用报错解决URLError: &#xff1c;urlopen error [Errno 11004] getaddrinfo failed&#xff1e;&#xff08;windows&#xff09;&#xff09; import seaborn as sns import matplotlib.pyplot as plt ​ # 使用Seaborn自带的数据集 tips sns.load_datas…