【数据结构】Java实现二叉搜索树
二叉搜索树的基本性质
二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树,它具有以下特征:
1. 节点结构:每个节点包含一个键(key)和值(value),以及指向左子树和右子树的指针。
2. 左子树和右子树的性质:
- 对于每个节点,左子树中所有节点的键都小于该节点的键。
- 右子树中所有节点的键都大于该节点的键。
- 这种特性使得二叉搜索树可以高效地进行查找、插入和删除操作。
3. 查找操作:从根节点开始,比较目标键与当前节点的键。如果目标键小于当前节点的键,则继续在左子树中查找;如果大于,则在右子树中查找。这个过程递归进行,直到找到目标节点或者到达树的叶子节点。
4. 插入操作:插入新节点时,同样从根节点开始,根据二叉搜索树的性质,选择左子树或右子树,直到找到一个空位置插入新节点。
5. 删除操作:删除节点较为复杂,分为三种情况:
- 若被删除节点为叶子节点,直接移除。
- 若被删除节点只有一个子节点,则用其子节点替代该节点。
- 若被删除节点有两个子节点,则需要找到该节点的后继节点(通常是右子树中最小的节点),用其替代被删除的节点,并递归删除后继节点。
二叉搜索树的平均时间复杂度为O(log n),但在最坏情况下(如插入顺序导致树变为链表),其时间复杂度可达到O(n)。为了避免这一问题,通常会使用自平衡的二叉搜索树,如红黑树或AVL树。
二叉搜索树在许多应用中非常重要,包括数据库索引、内存中的排序数据以及许多算法实现等。
代码实现二叉搜索树的基本操作
查找操作
二叉搜索树(BST)中的查找操作是基于树的特性进行的,具体原理如下:
查找操作原理
-
初始化当前节点(cur):
- 将当前节点设置为树的根节点(
root),开始从树的顶端进行查找。
- 将当前节点设置为树的根节点(
-
循环查找:
- 使用
while循环遍历树的节点,条件是cur不为null(即当前节点存在)。
- 使用
-
比较当前节点与目标值:
- 左子树查找:如果当前节点的值小于目标值,说明目标值可能在右子树中,因此将
cur指向cur.right。 - 右子树查找:如果当前节点的值大于目标值,说明目标值可能在左子树中,将
cur指向cur.left。 - 找到目标值:如果当前节点的值等于目标值,则查找成功,返回
true。
- 左子树查找:如果当前节点的值小于目标值,说明目标值可能在右子树中,因此将
-
查找结束:
- 如果循环结束,意味着没有找到目标值,此时返回
false。
- 如果循环结束,意味着没有找到目标值,此时返回

代码实现:
public boolean search(int val) {// 初始化当前节点为根节点treeNode cur = root;// 当当前节点不为空时循环查找while (cur != null) {// 如果当前节点的值小于目标值,则在右子树继续查找if (cur.val < val) {cur = cur.right;// 如果当前节点的值大于目标值,则在左子树继续查找} else if (cur.val > val) {cur = cur.left;// 如果找到目标值,返回true} else {return true;}}// 如果遍历结束仍未找到目标值,返回falsereturn false;
}
查找操作利用了二叉搜索树的排序性质,能够在相对较短的时间内找到目标值。通过比较和递归向左或向右子树移动,该操作在实际应用中被广泛使用,例如在需要快速检索数据的系统中,如数据库和内存中的索引结构等。
插入操作
二叉搜索树(Binary Search Tree, BST)的插入操作是基于树的特性进行的,以下是插入操作的原理详解:
插入操作原理
-
开始插入:
- 从树的根节点开始进行插入操作。
-
比较键值:
- 对于当前节点,比较要插入的值(key)与当前节点的键(val):
- 如果要插入的值小于当前节点的键,则应该将其插入到左子树中。
- 如果要插入的值大于当前节点的键,则应该将其插入到右子树中。
- 如果要插入的值等于当前节点的键,通常视为重复值,根据具体需求,可以选择不插入、更新值或者其他处理。
- 对于当前节点,比较要插入的值(key)与当前节点的键(val):
-
寻找空位:
- 将当前节点更新为其左子树或右子树,然后重复该过程,直到找到一个空位置(即当前节点为null)。
- 该空位置即为将新值插入树中的位置。
-
插入新节点:
- 在找到的空位置创建新的节点,并将其插入到树中。

代码实现:
public boolean insert(int val) {treeNode node = new treeNode(val);if(root == null) {root = node;return true;}treeNode cur = root;treeNode parent = null;while(cur != null) {if(val > cur.val) {parent = cur;cur = cur.right;}else if(val < cur.val) {parent = cur;cur = cur.left;}else {return false;}}if(val > parent.val) {parent.right = node;}else {parent.left = node;}return true;}
插入操作遵循了二叉搜索树的基本特性,通过比较和递归地前进来在合适的位置插入新节点。这一操作是保持树的结构的关键,确保每次插入后都能满足二叉搜索树的性质,以便后续的查找、删除等操作更加高效。
删除操作
-
查找要删除的节点:
- 从根节点开始,通过与当前节点的值比较,找到目标节点。如果目标值大于当前节点值则继续查找右子树;如果小于,则查找左子树。同时记录当前节点的父节点。
-
执行删除操作:
- 一旦找到要删除的节点,调用
removeNode方法,根据要删除节点的子节点情况执行不同的删除逻辑。
- 一旦找到要删除的节点,调用
代码实现:
public void remove(int key) {treeNode cur = root; // 当前节点初始化为根节点treeNode parent = null; // 记录当前节点的父节点// 找到要删除的节点while(cur != null) {if(key > cur.val) {parent = cur; // 更新父节点cur = cur.right; // 向右子树查找} else if(key < cur.val) {parent = cur; // 更新父节点cur = cur.left; // 向左子树查找} else {// 找到目标节点,执行删除逻辑removeNode(parent, cur);return; // 找到并删除后退出}}System.out.println("没有该节点"); // 如果节点未找到,输出提示信息
}// 执行删除操作的方法
private void removeNode(treeNode parent, treeNode cur) {// 情况1:要删除的节点没有左子节点if(cur.left == null) {if(cur == root) {root = cur.right; // 如果要删除的节点是根节点,更新根节点} else if(cur == parent.left) {parent.left = cur.right; // 更新父节点的左子指针} else {parent.right = cur.right; // 更新父节点的右子指针}}// 情况2:要删除的节点没有右子节点else if(cur.right == null) {if(cur == root) {root = cur.left; // 如果要删除的节点是根节点,更新根节点} else if(cur == parent.left) {parent.left = cur.left; // 更新父节点的左子指针} else {parent.right = cur.left; // 更新父节点的右子指针}}// 情况3:要删除的节点有两个子节点else {// 找到要删除节点的右子树中的最小节点treeNode target = cur.right;treeNode targetParent = cur;// 寻找右子树中最小节点while(target.left != null) {targetParent = target; // 更新最小节点的父节点target = target.left; // 持续向左查找}// 用找到的最小节点的值替代要删除的节点的值cur.val = target.val;// 删除最小节点if(target == targetParent.right) {targetParent.right = target.right; // 更新父节点的右子指针} else {targetParent.left = target.right; // 更新父节点的左子指针}}
}
-
查找节点:
- 使用
while循环查找目标节点,记录其父节点,以便于后续的删除操作。比较节点值,并依据结果移动到左或右子树。
- 使用
-
删除逻辑:
- 没有左子节点:直接将右子节点连接到父节点,删除当前节点。
- 没有右子节点:直接将左子节点连接到父节点,同样删除当前节点。
- 有两个子节点:找到右子树中最小的节点(后继节点),用其值替代当前节点的值,然后递归删除后继节点。
-
输出信息:
- 如果在树中找不到目标节点,输出“没有该节点”提示。
性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:
相关文章:
【数据结构】Java实现二叉搜索树
二叉搜索树的基本性质 二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树,它具有以下特征: 1. 节点结构:每个节点包含一个键(key)和值(value),以及指…...
钉钉小程序如何通过setdate重置对象
在钉钉小程序中,通过setData方法来重置对象(即更新对象中的数据)是一个常见的操作。然而,需要注意的是,钉钉小程序(或任何小程序平台)的setData方法在处理对象更新时有一些特定的规则和最佳实践…...
DjangoRF-10-过滤-django-filter
1、安装pip install django-filter https://pypi.org/ 搜索django-filter基础用法 2、进行配置 3、进行内容调试。 4、如果碰到没有关联的字段。interfaces和projects没有直接关联字段,但是interface和module有关联,而且module和projects关联&#x…...
Android SurfaceFlinger——GraphicBuffer的生成(三十二)
通过前面的学习我们知道,在 SurfaceFlinger 中使用的生产者/消费者模型,Surface 做为生产者一方存在如下两个比较重要的函数: dequeueBuffer:获取一个缓冲区(GraphicBuffer),也就是 GraphicBuffer 生成。queueBuffer :把缓冲区(GraphicBuffer)放入缓冲队列中。 …...
<数据集>棉花识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:13765张 标注数量(xml文件个数):13765 标注数量(txt文件个数):13765 标注类别数:4 标注类别名称:[Partially opened, Fully opened boll, Defected boll, Flower] 序…...
[240730] OpenAI 推出基于规则的奖励机制 (RBR) 提升模型安全性 | 英特尔承认其13、14代 CPU 存在问题
目录 OpenAI 推出基于规则的奖励机制(RBR)提升模型安全性英特尔承认其 13、14代 CPU 存在问题 OpenAI 推出基于规则的奖励机制(RBR)提升模型安全性 为了解决传统强化学习中依赖人工反馈的低效问题,OpenAI 开发了基于规…...
【JavaScript】展开运算符详解
文章目录 一、展开运算符的基本用法1. 展开数组2. 展开对象 二、展开运算符的实际应用1. 合并数组2. 数组的浅拷贝3. 合并对象4. 对象的浅拷贝5. 更新对象属性 三、展开运算符的高级用法1. 在函数参数中使用2. 嵌套数组的展开3. 深拷贝对象4. 动态属性名 四、注意事项和最佳实践…...
麒麟V10系统统一认证子系统国际化
在适配麒麟V10系统统一认证子系统国际化过程中, 遇到了很多的问题,关键是麒麟官方的文档对这部分也是粗略带过,遇到的问题有: (1)xgettext无法提取C源文件中目标待翻译的字符串。 (2)使用msgf…...
C语言进阶 13. 文件
C语言进阶 13. 文件 文章目录 C语言进阶 13. 文件13.1. 格式化输入输出13.2. 文件输入输出13.3. 二进制文件13.4. 按位运算13.5. 移位运算13.6. 位运算例子13.7. 位段 13.1. 格式化输入输出 格式化输入输出: printf %[flags][width][.prec][hlL]type scanf %[flags]type %[fl…...
LinuxCentos中ELK日志分析系统的部署(详细教程8K字)附图片
🏡作者主页:点击! 🐧Linux基础知识(初学):点击! 🐧Linux高级管理防护和群集专栏:点击! 🔐Linux中firewalld防火墙:点击! ⏰️创作…...
Vscode ssh Could not establish connection to
错误表现 上午还能正常用vs code连接服务器看代码,中午吃个饭关闭vscode再重新打开输入密码后就提示 Could not establish connection to xxxx 然后我用终端敲ssh的命令连接,结果是能正常连接。 解决方法 踩坑1 网上直接搜Could not establish con…...
数字陷波器的设计和仿真(Matlab+C)
目录 一、数字陷波器的模型 二、Matlab仿真 1. 示例1 2. 示例2 三、C语言仿真 1. 由系统函数计算差分方程 2. 示例代码 一、数字陷波器的模型 二、Matlab仿真 1. 示例1 clear clc f0=100;%滤掉的100Hz fs=1000;%大于两倍的信号最高频率 r=0.9; w0=2*pi*f0/fs;%转换到…...
[玄机]流量特征分析-常见攻击事件 tomcat
题目网址【玄机】:https://xj.edisec.net/ Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages (JSP) 技术,提供了一个运行这些应用程序的Web服务器环境。Tomcat由Apache软件基金会的Jakarta项目开发,是…...
【TOOLS】Project 2 Maven Central
发布自己的项目到maven中央仓库 Maven Central Account 访问:https://central.sonatype.com/,点击右上角,根据提示注册账号 构建User token ,用于访问中央仓库的API: 点击右上角,查看账户点击Generate Us…...
【Opencv】模糊
消除噪声 用该像素周围的平均值代替该像素值 4个函数 blur():最经典的 import os import cv2 img cv2.imread(os.path.join(.,dog.jpg)) k_size 7 #窗口大小,数字越大,模糊越强 img_blur cv2.blur(img,(k_size,k_size)) #窗口是正方形ÿ…...
函数式编程范式
文章目录 函数式编程范式不可变性(Immutable)纯函数(Pure Functions)函数作为一等公民(First-Class Functions)高阶函数(Higher-Order Functions函数组合(Function Composition&…...
特征缩放的秘籍:sklearn中的数据标准化技术
特征缩放的秘籍:sklearn中的数据标准化技术 在机器学习中,特征缩放(Feature Scaling)是数据预处理的重要步骤,它确保了不同量纲和范围的特征在模型训练中具有相同的重要性。Scikit-learn(简称sklearn&…...
hdfs文件系统
简述什么是HDFS,以及HDFS作用 ? HDFS在Hadoop中的作用是为海量的数据提供了存储,能提供高吞吐量的数据访问,HDFS有高容错性的 特点,并且设计用来部署在低廉的硬件上;而且它提供高吞吐量来访问应用程序的数…...
基于STM32设计的个人健康检测仪(华为云IOT)(191)
基于STM32设计的个人健康检测仪(华为云IOT)(191) 文章目录 一、设计需求1.1 设计需求总结1.2 设计思路【1】整体设计思路【2】整体构架【3】ESP8266模块配置【4】上位机开发思路【5】供电方式1.3 项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】课题研究的意义【…...
面试:CUDA Tiling 和 CPU tiling 技术详解
目录 一、CUDA Tiling 和 CPU Tiling 技术概述 (一)技术原理 (二)应用场景 (三)优势和劣势 二、Tiling 技术在深度学习中的应用 三、Tiling 技术的缺点 一、CUDA Tiling 和 CPU Tiling 技术概述 Til…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
