入门数据结构JAVADS——如何构建一棵简单二叉排序树

目录

前言

什么是二叉排序树

二叉排序树的特点

二叉排序树示意图

构建二叉排序树

 插入元素

 搜索元素

 删除元素

完整代码

结尾

前言

在整个十一月,笔者因为一些原因停笔了,但马上迈入12月进而进入2025年,笔者决定不再偷懒了,继续更新以促进学习的积极性.闲话说到这,今天更新的是如何构建一个简单的二叉排序树

什么是二叉排序树

二叉排序树(Binary Search Tree,简称 BST)是一种特殊的二叉树,它的每个节点都满足以下性质:

  1. 左子树上所有节点的值,都小于根节点的值。
  2. 右子树上所有节点的值,都大于根节点的值。
  3. 左子树和右子树本身也都是二叉排序树(递归定义)。

二叉排序树的特点

  • 值的唯一性:二叉排序树中每个节点的值必须是唯一的(不允许重复值)。
  • 有序性:中序遍历(左-根-右)二叉排序树时,会得到一个递增的有序序列
  • 动态性:支持动态插入和删除元素,能够随时维护有序性。
  • 时间复杂度:平均情况下插入、查找、删除的时间复杂度为 O(log⁡n),但在极端情况下(树退化为链表),复杂度可能退化到 O(n)。

举个例子

二叉排序树示意图

假设我们依次插入以下数字:50, 30, 70, 20, 40, 60, 80
构造的二叉排序树如下:

        50/    \30      70/  \    /  \20   40  60   80

此时,中序遍历二叉树,即可得到顺序排列. 

构建二叉排序树

想要写一棵简单的二叉排序树,大致需要三个方法,分别是 插入结点,查找结点,删除结点

我们首先创建好结点类

   static  class TreeNode{public  int val;public  TreeNode left;public  TreeNode right;public TreeNode(int val)   {this.val = val;}}public  TreeNode root;

 插入元素

插入元素是比较简单的,思路如下:

1.   首先判断树是不是空的,如果是空的,插入元素即为根

2.   如果不是空的,那么就借助双亲结点,去找需要插入结点的位置,然后再和双亲结点比较,看看左子树还是右子树

  public boolean insert(int val){if(root==null){root=new TreeNode(val);return true;}TreeNode parent=null;TreeNode cur=root;while(cur!=null){if(cur.val>val){parent=cur;cur=cur.left;} else if (cur.val<val) {parent=cur;cur=cur.right;}else// 如果是等于就不能插入了,二叉排序树没办法容纳一样的key{return false;}}if(parent.val>val){parent.left=new TreeNode(val);}else{parent.right=new TreeNode(val);}return true;}

***************** 需要注意的是,二叉排序树不能插入重复的元素.*********************

 搜索元素

搜索元素也很简单,因为二叉排序树本身就是为了提高搜索效率而产生的,

  public  boolean search(int key){TreeNode cur=root;while(cur!=null){if(cur.val>key){cur=cur.left;}else if(cur.val<key){cur =cur.right;}else{return true;}}return false;}

 删除元素

  删除元素是比较难的,如果笔者自己想想就会发现,好像有好多种情况要去想,这里笔者也不绕弯子,前辈们已经替我们总结出来了,大致会有三种情况

但在分情况之前 首先:设待删除结点为 cur, 待删除结点的双亲结点为 parent, 其次,要确定,待删除结点是否存在.

public  boolean remove(int val){TreeNode parent=null;TreeNode cur=root;while(cur!=null){if(cur.val>val){parent=cur;cur=cur.left;}else if(cur.val<val){parent=cur;cur =cur.right;}else{// 找到了removeNode(cur,parent);return true;}}return false;}

 接下来我们完善 removeNode 

第一种情况,待删结点没有左子树

这种情况,我们就让它的右子树结点和双亲结点连接上就好了.

但也有个前提,他不是根节点,所以要考虑到这一点

代码如下:

   if(cur.left==null)// 左边为空{if(cur==root){root=root.right;return;}else if(cur==parent.left){parent.left=cur.right;return ;}else{parent.right=cur.right;return ;}}

 如果是双亲结点的左子树,就是  parent.left=cur.right;

 如果是双亲结点的右子树,就是  parent.right=cur.right;

第二种情况,待删结点没有右子树

同理,代码如下

      else if (cur.right==null)// 右边为空{if(cur==root){root=root.left;}else if(cur==parent.left){parent.left=cur.left;}else{parent.right=cur.left;}}

 第三种,左右都不为空

我们的思路:
1.找到 cur

2.找cur左子树最右边的或者cur右子树最左边的,来替换我们的这个cur结点

3.替换完成以后,再把结点删除,请注意,有两种可能.如图所示

 else{TreeNode ansparent = cur;TreeNode ans = cur.right;// 去找右子树最左边的while(ans.left!=null){ansparent=ans;ans=ans.left;}// 找到以后,也要分情况cur.val=ans.val;if(ansparent.left == ans){ansparent.left = ans.right;}else{ansparent.right=ans.right;}}

完整代码

package searchtree;public class SearchTree// 二叉搜索树
{static  class TreeNode{public  int val;public  TreeNode left;public  TreeNode right;public TreeNode(int val)   {this.val = val;}}public  TreeNode root;public  boolean search(int key){TreeNode cur=root;while(cur!=null){if(cur.val>key){cur=cur.left;}else if(cur.val<key){cur =cur.right;}else{return true;}}return false;}public boolean insert(int val){if(root==null){root=new TreeNode(val);return true;}TreeNode parent=null;TreeNode cur=root;while(cur!=null){if(cur.val>val){parent=cur;cur=cur.left;} else if (cur.val<val) {parent=cur;cur=cur.right;}else// 如果是等于就不能插入了,二叉排序树没办法容纳一样的key{return false;}}if(parent.val>val){parent.left=new TreeNode(val);}else{parent.right=new TreeNode(val);}return true;}public  boolean remove(int val){TreeNode parent=null;TreeNode cur=root;while(cur!=null){if(cur.val>val){parent=cur;cur=cur.left;}else if(cur.val<val){parent=cur;cur =cur.right;}else{// 找到了removeNode(cur,parent);return true;}}return false;}private void removeNode(TreeNode cur, TreeNode parent){if(cur.left==null)// 左边为空{if(cur==root){root=root.right;return;}else if(cur==parent.left){parent.left=cur.right;return ;}else{parent.right=cur.right;return ;}}else if (cur.right==null)// 右边为空{if(cur==root){root=root.left;}else if(cur==parent.left){parent.left=cur.left;}else{parent.right=cur.left;}}// 左右都不为空// 思路// 1.找到 cur// 2.找cur左子树最右边的或者cur右子树最左边的,来替换我们的这个cur结点else{TreeNode ansparent = cur;TreeNode ans = cur.right;// 去找右子树最左边的while(ans.left!=null){ansparent=ans;ans=ans.left;}// 找到以后,也要分情况cur.val=ans.val;if(ansparent.left == ans){ansparent.left = ans.right;}else{ansparent.right=ans.right;}}}
}

结尾

这一篇算是我的笔记,所以写的会比较潦草,提前sorry了

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

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

相关文章

更多开源创新 挑战OpenAI-o1的模型出现和AI个体模拟突破

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

BUUCTF—Reverse—Java逆向解密(10)

程序员小张不小心弄丢了加密文件用的秘钥&#xff0c;已知还好小张曾经编写了一个秘钥验证算法&#xff0c;聪明的你能帮小张找到秘钥吗&#xff1f; 注意&#xff1a;得到的 flag 请包上 flag{} 提交 需要用专门的Java反编译软件:jd-gui 下载文件&#xff0c;发现是个class文…

Redis(4):主从复制

一、主从复制概述 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(master)&#xff0c;后者称为从节点(slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。   默认情况下&#xff0c;每台Redis…

操作系统 | 学习笔记 | 王道 | 2.2处理机调度

2.2 处理机调度 文章目录 2.2 处理机调度2.2.1 调度的概念2.2.2 调度的目标2.2.3 调度的实现2.2.4 典型的调度算法错题总结&#xff1a; 2.2.1 调度的概念 调度的基本概念 处理机调度是对处理机进行分配&#xff0c;即从就绪队列中按照一定的算法&#xff08;公平、高效的原则&…

PostgreSQL的学习心得和知识总结(一百五十八)|在线调优工具pgtune的实现原理和源码解析

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【问题】webdriver.Chrome()设置参数executable_path报不存在

场景1: 标红报错unresolved reference executable_path 场景2: 执行报错TypeError: __init__() got an unexpected keyword argument executable_path 原因&#xff1a; 上述两种场景是因为selenium4开始不再支持某些初始化参数。比如executable_path 解决&#xff1a; 方案…

JS听到了双生花的回响

日期对象 学会了日期对象可以让网页显示日期 是用来表示时间的对象&#xff0c;可以得到当前系统的时间 实例化 new关键字&#xff0c;就是实例化的代表 就比如说&#xff0c;你没有对象&#xff0c;但是你是程序员&#xff0c;这个时候你可以先定义一个类&#xff08;你的…

C++类中多线程的编码方式

问题 在C++代码中,一般的代码是需要封装在类里面,比如对象,方法等。否则就不能很好的利用C++面向对象的能力了。 但是这个方式在处理线程时会碰到一个问题。 考虑下面一个简单的场景: class demoC { public:std::thread t;int x;void threadFunc(){std::cout<<x&…

Chapter 17 v-model进阶

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 1 v-model原理2 表单类组件封装3 v-model简化代码 1 v-model原理 1. 基本原理 v-model 本质上是一个语法糖&#xff0c;它将 value 属性 和 input 事件 的绑定合并为一个指令…

spring-boot-maven-plugin 标红

情况&#xff1a;创建好 Spring Boot 项目后&#xff0c;pom.xml 文件中 spring-boot-maven-plugin 标红。 解决方案&#xff1a;加上 Spring Boot 的版本即可解决。

电子应用设计方案-31:智能AI音响系统方案设计

智能 AI 音响系统方案设计 一、引言 智能 AI 音响作为一种新兴的智能家居设备&#xff0c;通过融合语音识别、自然语言处理、音频播放等技术&#xff0c;为用户提供便捷的语音交互服务和高品质的音乐体验。本方案旨在设计一款功能强大、性能稳定、用户体验良好的智能 AI 音响系…

“harmony”整合不同平台的单细胞数据之旅

其实在Seurat v3官方网站的Vignettes中就曾见过该算法&#xff0c;但并没有太多关注&#xff0c;直到看了北大张泽民团队在2019年10月31日发表于Cell的《Landscap and Dynamics of Single Immune Cells in Hepatocellular Carcinoma》&#xff0c;为了同时整合两类数据&#xf…

接口测试工具:reqable

背景 在众多接口测试工具中挑选出一个比较好用的接口测试工具。使用过很多工具&#xff0c;如Postman、Apifox、ApiPost等&#xff0c;基本上是同类产品&#xff0c;一般主要使用到的功能就是API接口和cURL&#xff0c;其他的功能目前还暂未使用到。 对比 性能方面&#xff…

内容安全与系统构建加速,助力解决生成式AI时代的双重挑战

内容安全与系统构建加速&#xff0c;助力解决生成式AI时代的双重挑战 0. 前言1. PRCV 20241.1 大会简介1.2 生成式 Al 时代的内容安全与系统构建加速 2. 生成式 AI2.1 生成模型2.2 生成模型与判别模型的区别2.3 生成模型的发展 3. GAI 内容安全3.1 GAI 时代内容安全挑战3.2 图像…

SRS搭建直播推流服务

学习链接 5分钟教你搭建SRS流媒体服务器 - B站视频 SRS Stack 入门B站合集视频 - SRS官方教程 SRS官网 SRS官网文档 ossrs/srs github SRS for window - 可以安装windows版本的srs&#xff0c;SRS 5.0.89正式支持Windows&#xff0c;每个5.0的版本都会提供安装包 文章目录…

javaScript数据类型存储

2.1、简单类型与复杂类型 简单类型又叫做基本数据类型或者值类型&#xff0c;复杂类型又叫做引用类型 值类型&#xff1a;简单数据类型/基本数据类型&#xff0c;在存储时变量中存储的时值本身&#xff0c;因此叫做值类型 string、number、boolean、undefined、null 注意&…

深度学习之 DenseNet和2图像分割常用数据集

1 DenseNet 卷积神经网络结构的设计主要朝着两个方向发展&#xff0c;一个是更宽的网络&#xff08;代表&#xff1a;GoogleNet、VGG&#xff09;&#xff0c;一个是更深的网络&#xff08;代表&#xff1a;ResNet&#xff09;。但是随着层数的加深会出现一个问题——梯度消失&…

Nginx:反向代理

目录 反向代理原理 反向代理配置 日志对比 反向代理原理 网站通过代理服务器发布&#xff0c;用户无需得知网站的实际地址&#xff0c;通过代理服务器进行请求与响应。 用户所有的网站请求报文与响应报文都被代理服务器拦截&#xff0c;在网络层将源地址和目的地址进行了修改…

Linux系统编程——进程替换

目录 前言 二、进程程序替换的概念 三、进程程序替换的原理 ​编辑 四、为什么需要进行进程程序替换 五、如何进行进程程序替换 1、进程替换函数&#xff1a; 1)execl()函数 2)execv()函数 3) execlp()函数 4) execvp()函数 5&#xff09;execle函数 6&#xff09;ex…

探索HarmonyOS:一键掌握Router与NavPathStatck的传参和页面回调技巧

路由的选择 HarmonyOS提供两种路由实现的方式&#xff0c;分别是 Router 和 NavPatchStack。两者使用场景和特效各有优劣。 组件适用场景特点备注Router模块间与模块内页面切换通过每个页面的url实现模块间解耦NavPathStack模块内页面切换通过组件级路由统一路由管理 什么时候使…