java数据结构_Map和Set_9.1

1. 搜索树

1.1 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有的结点都小于根结点的值
  • 若它的右子树不为空,则右子树上所有的结点都大于根结点的值
  • 它的左右子树也都分别是二叉搜索树

如下图,就是一棵二叉搜索树,二叉搜索树的中序遍历就是升序排列的

1.2 操作-查找

因为二叉搜索树天然就带有小的在左,大的在右的顺序,所以在查找的时候更加方便,类似于有天然的二分查找

代码实现如下:

1.3 操作 - 插入

按照二叉搜索树,小的在左边,大的在右边逻辑确定插入位置,插入新结点。因为要插入的结点最终都是称为叶子结点,所以光定义一个cur来表示要插入的结点还不够,还要定义一个parent结点来表示cur的父亲结点。

代码如下:

但上面代码的逻辑性还是有问题的,插入操作,并没有考虑到,如果要插入的树是一棵空树,即root == null的情况,会出现空指针的异常。

修正:

1.4 操作 - 删除(难)

搜索二叉树的删除是会发生很尴尬的情况的

如下图:

如果要删除值为8的结点,是很方便的,可以将7结点的right连接到值为9的结点。

但如果删除的是左边的值为1的结点呢?

删除了之后,3的left是连接值为2的结点呢?还是连接值为0的结点呢?这样就会出现尴尬的情况,采用的方法是替换法(也称替罪羊法),即可以在要删除的结点的右子树中,寻找一个值最小的叶子节点,换个方式讲是,找到要删除的结点的右子树的最左边子树(有点绕,可以多多理解一下),然后用这个最左边子树的值来覆盖要删除的结点的值,然后将最左边子树的结点删除。注意是覆盖,并不是将要删除的结点真的删除了,最终删除的结点是要删除的结点的右子树的最左边子树的结点。

举例:

如果要删除的结点是值为90的结点

先找到90结点的右子树为值为120的结点,然后再找到右子树120的最左边子树,即值为95的结点,用这个结点的值95来覆盖掉要删除的结点90的值,然后删除值为95的这个结点,如何删除呢?如果值为95的这个结点仍有有右子树呢?(注意:值为95的这个结点不可能再有左子树了,因为我们要在前面的操作中找的就是 要删除的结点的右子树的最左子树)就把值为95的结点的右子树连接到值为120的结点的left上即可。

(上面只是一种方式,是找到 要删除结点 的 右子树 的最左侧子树,同理, 也可以找到 要删除的结点 的 左子树 的最右侧子树,都相反一下即可,此处以右子树的最左侧左子树来研究)

代码实现:

设待删除结点为cur,带删除结点的双亲结点为parent

在删除中,有多种情况:

1.1情况:cur为root 且 cur.left == null 如下图,此时直接将cur的left赋值为root即可

删除后的效果:

1.2情况:cur不是root,cur.left == null,cur在左子树上,即cur 是 parent.left 如下图的效果,此时要删除的结点是值为40的结点,直接parent.left = cur.right即可

1.3 情况:cur不是root,cur.left == null cur是parent.right,即cur在右子树上,如下图情况,则直接parent.right = cur.right即可

2.1情况 cur.right == null cur是root 则直接root = cur.left即可

2.2情况:cur不是root cur是parent.left  且cur.right == null 则parent .left = cur.left

2.3 cur不是root cur是parent.right 且cur.right == null 则parent.right = cur.left


即使用前面所述的替代法。

代码实现如下:

下面的代码实现是前面的删除逻辑

替代法因为要向下找到要删除结点 的 右边子树 的 最左边子树,所以还需要一个寻找的逻辑,定义一个targetParent和target来寻找找最左子树

当结束上面的while循环时候,即target指向的是最左子树结点,然后cur.val = target.val进行覆盖

如下图所示:要删除的结点是cur,t是要删除节点 的 右边结点 的 最左侧结点,将cur的值覆盖为95,然后将 t 结点删掉(targetParent.left = target.right)(注意:删除的时候不要直接targetParent.left = null 因为target可能有右节点 也可能 没有右节点)

则代码如下:

但上面代码还是存在逻辑漏洞

如果是下图的情况,cur为值为95的结点,在while循环结束后,t指向的是值为120的结点,直接执行targetParent.left = target.right 就乱套了

所以还应该加一个 if 条件 即:if(taregt == targeParent.left)的时候,才能targetParent.left = target.right;否则应该是targetParent.right = target.right;

则替代法的实现代码应该是:

完整的删除代码如下:

 public void remove(int val) {TreeNode cur = root;TreeNode parent = null;while(cur != null) {if(val < cur.val) {parent = cur;cur = cur.left;} else if(val > cur.val) {parent = cur;cur = cur.right;} else {//删除的逻辑removeNode(parent,cur);return;}}}private void removeNode(TreeNode parent, TreeNode cur) {if(cur.left == null) {if(cur == root) {root = cur.right;} else if(cur == parent.right) {parent.right = cur.right;} else {parent.left = cur.right;}} else if(cur.right == null) {if(cur == root) {root = cur.left;} else if(cur == parent.right) {parent.right = cur.left;} else {parent.left = cur.left;}} else {//替代法:TreeNode targetParent = cur;TreeNode target = cur.right;while(target.left != null) {targetParent = target;target = target.left;}cur.val = target.val;if(targetParent.left == target) {targetParent.left = target.right;} else {targetParent.right = target.right;}}}

1.5 性能分析:

插入和删除的操作都必须先查找,所以查找的效率可以代表二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找商都是结点在二叉搜索树的函数,即节点越深,则比较的次数就越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最好情况下:二叉搜索树为完全二叉树,平均比较次数为logN

最坏情况下:二叉搜索树退化为单分支树,其平均比较次数为:N / 2

可以及逆行改进,使得不管按照什么次序插入关键码,都可以使得二叉搜索树的性能最佳 -- 数据结构高阶时候学习!

2. 搜索

2.1 概念及场景

Map和Set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。之前常见的搜索方式有:

  1. 直接遍历:时间复杂度为O(N),元素如果比较多的话,效率较低。
  2. 二分查找:时间复杂度为O(logN),但搜索前必须要求序列是有序的

上述方式比较适合静态类型的数据进行查找,即一般不会对区间内进行插入和删除操作了,但在现实情况中的查找:1. 根据姓名查询考试成绩 2. 通讯录,即根据姓名查询联系方式 3. 不重复集合,即需要先搜索关键字是否已经在集合中,可能在查找的过程中进行一些插入和删除的操作,即动态查找,那上述两种方式就不合适了,这里介绍的Map和Set是一种适合动态查找的集合容器

2.2 模型

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型有两种:

  1. 纯Key模型,比如 有一个英文词典,快速查找一个单词是否收录在该词典中
  2. Key-Value模型,比如,梁山好汉的绰号

Map中存储的就是key-value键值对,Set中只存储了Key

3. Map的使用

Map官方文档:Map (Java Platform SE 8 )


3.1 Map的说明:

Map是一个接口类,并没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复

3.2 关于Map.Entry<K,V>的说明

Map.Entr<K,V>是Map内部实现的用来存放<key,value>键值对映射关系的内部类,该内部类中主要提供了key和value的获取,value的设置以及key的比较方式

注意:Map.Entry<K,V>并没有提供设置Key的方法

3.3 Map的常用方法说明

注意:

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象是能实例化其实现类TreeMap或者HashMap
  2. Map中存放键值对的Key是唯一的value是可以重复的
  3. 在TreeMap中插入键值对时,key不能为空,否则会抛出空指针异常,但value可以为空,在HashMap中,key和value都可以为空
  4. Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)
  5. Map中的value可以全部分离出来,存储在Collection的任何一个子集中(value可能有重复)。
  6. Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。

3.4 TreeMap的使用案例

getOrDefault方法的源码:

完!

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

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

相关文章

Rust Async 并发编程:处理任意数量的 Future 与 Stream

1. Streams&#xff1a;异步数据流 1.1 Streams 与 Iterator 的异同 Rust 的 Iterator 是同步的&#xff0c;通过 next() 方法逐个获取数据。而 Stream 是 async 版本的 Iterator&#xff0c;它使用 next().await 来获取数据项。 示例&#xff1a;将 Iterator 转换为 Stream…

蓝桥杯 路径之谜

路径之谜 题目描述 小明冒充 XX 星球的骑士&#xff0c;进入了一个奇怪的城堡。 城堡里边什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡地面是 nnnn 个方格。如下图所示。 按习俗&#xff0c;骑士要从西北角走到东南角。可以横向或纵向移动&#xff0c;但不能斜着走…

3-5 WPS JS宏 工作表的移动与复制学习笔记

************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…

聊聊Java的SPI机制

个人自建博客地址 什么是SPI呢&#xff1f; SPI全称Service Provider Interface&#xff0c;翻译过来就是服务提供者接口。调用方提供接口声明&#xff0c;服务提供方对接口进行实现&#xff0c;提供服务的一种机制&#xff0c;服务提供方往往是第三方或者是外部扩展。 下面…

langchain4j+local-ai小试牛刀

序 本文主要研究一下如何本地运行local-ai并通过langchain4j集成调用。 步骤 curl安装 curl https://localai.io/install.sh | sh% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed 100 21509 …

什么是“零日漏洞”(Zero-Day Vulnerability)?为何这类攻击被视为高风险威胁?

正文 零日漏洞&#xff08;Zero-Day Vulnerability&#xff09; 是指软件、硬件或系统中存在的、尚未被开发者发现或修复的安全漏洞。攻击者在开发者意识到漏洞存在之前&#xff08;即“零日”内&#xff09;利用该漏洞发起攻击&#xff0c;因此得名。这类漏洞的“零日”特性使…

鸿蒙 ArkUI 实现 2048 小游戏

2048 是一款经典的益智游戏&#xff0c;玩家通过滑动屏幕合并相同数字的方块&#xff0c;最终目标是合成数字 2048。本文基于鸿蒙 ArkUI 框架&#xff0c;详细解析其实现过程&#xff0c;帮助开发者理解如何利用声明式 UI 和状态管理构建此类游戏。 一、核心数据结构与状态管理…

Milvus高性能向量数据库与大模型结合

Milvus | 高性能向量数据库&#xff0c;为规模而构建Milvus 是一个为 GenAI 应用构建的开源向量数据库。使用 pip 安装&#xff0c;执行高速搜索&#xff0c;并扩展到数十亿个向量。https://milvus.io/zh Milvus 是什么&#xff1f; Milvus 是一种高性能、高扩展性的向量数据…

kettle插件-自定义函数-数据脱敏

平常我们在使用kettle抽取数据的时候会涉及到敏感数据邀请脱敏或者进行掩码的需求&#xff0c;今天我们使用自定义函数插件来实现这些需求。 1、将自定义函数插件&#xff08;kettle-func-plugin.zip&#xff09;解压后放到kettle的plugins目录下面&#xff0c;然后重启服务。…

LeetCode 每日一题 2025/2/24-2025/3/2

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 2/24 1656. 设计有序流2/25 2502. 设计内存分配器2/26 1472. 设计浏览器历史记录2/27 2296. 设计一个文本编辑器2/28 2353. 设计食物评分系统3/1 131. 分割回文串3/2 2/24 …

C++动态与静态转换区别详解

文章目录 前言一、 类型检查的时机二、安全性三、适用场景四、代码示例对比总结 前言 在 C 中&#xff0c;dynamic_cast 和 static_cast 是两种不同的类型转换操作符&#xff0c;主要区别体现在类型检查的时机、安全性和适用场景上。以下是它们的核心区别&#xff1a; 一、 类…

探秘《矩阵之美》:解锁矩阵的无限魅力

在这个数据驱动的时代&#xff0c;矩阵作为数学中的瑰宝&#xff0c;不仅在理论研究中占据核心地位&#xff0c;更在工程技术、计算机科学、物理学、经济学等众多领域发挥着不可替代的作用。今天&#xff0c;让我们通过中科院大学耿修瑞老师&#xff08;中科院空天信息研究院研…

【MySQL】(2) 库的操作

SQL 关键字&#xff0c;大小写不敏感。 一、查询数据库 show databases; 注意加分号&#xff0c;才算一句结束。 二、创建数据库 {} 表示必选项&#xff0c;[] 表示可选项&#xff0c;| 表示任选其一。 示例&#xff1a;建议加上 if not exists 选项。 三、字符集编码和排序…

Vue3实现文件上传、下载及预览全流程详解(含完整接口调用)

文章目录 一、环境准备1.1 创建Vue3项目1.2 安装依赖1.3 配置Element Plus 二、文件上传实现2.1 基础上传组件2.2 自定义上传逻辑&#xff08;Axios实现&#xff09; 三、文件下载实现3.1 直接下载&#xff08;已知文件URL&#xff09;3.2 后端接口下载&#xff08;二进制流&am…

分布式数据存储:提升系统弹性与性能的技术之路

分布式数据存储:提升系统弹性与性能的技术之路 在当今数据爆炸式增长的时代,传统的单机存储系统已无法满足大规模、高并发、低延迟的需求。尤其是在大数据、云计算和物联网的推动下,数据存储面临着前所未有的挑战。分布式数据存储应运而生,通过将数据分布在多个物理节点上…

在编译Linux的内核镜像和模块时,必须先编译内核镜像,再编译模块,顺序不可随意调整的原因

问&#xff1a;在编译Linux的内核镜像和模块时,必须先编译内核镜像,再编译模块,顺序不可随意调整 答&#xff1a;在编译 Linux 内核和模块时&#xff0c;必须先编译内核镜像&#xff0c;再编译模块&#xff0c;顺序不可随意调整。 原因&#xff1a; 模块依赖内核的头文件和符…

免费使用 DeepSeek API 教程及资源汇总

免费使用 DeepSeek API 教程及资源汇总 一、DeepSeek API 资源汇总1.1 火山引擎1.2 百度千帆1.3 阿里百炼1.4 腾讯云 二、其他平台2.1 华为云2.2 硅基流动 三、总结 DeepSeek-R1 作为 2025 年初发布的推理大模型&#xff0c;凭借其卓越的逻辑推理能力和成本优势&#xff0c;迅速…

千峰React:案例二

完成对html文档还有css的引入&#xff0c;引入一下数据&#xff1a; import { func } from prop-types import ./购物车样式.css import axios from axios import { useImmer } from use-immer import { useEffect } from reactfunction Item() {return (<li classNameacti…

用DeepSeek生成批量删除处理 PDF第一页工具

安装依赖库 在运行程序之前&#xff0c;请确保安装所需的库&#xff1a; pip install pymupdf python-docx Python 程序代码 import os import fitz # PyMuPDF from docx import Documentdef delete_pdf_first_page(input_path, output_path):"""删除 PDF…

redis的下载和安装详解

一、下载redis安装包 进入redis官网查看当前稳定版本&#xff1a; https://redis.io/download/发现此时的稳定版本是6.2.4&#xff0c; 此时可以去这个网站下载6.2.4稳定版本的tar包。 暂时不考虑不在windows上使用redis&#xff0c;那样将无法发挥redis的性能 二、上传tar…