【数据结构】二叉搜索树的模拟实现

目录

1、概念

2、模拟实现

2.1、查找

2.2、插入

2.3、删除(难点)

3、性能分析

 4、完整代码


 

1、概念

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

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

Java底层实现搜索树的两个主要类是TreeSetTreeMap

        TreeSet是基于红黑树(Red-Black tree)实现的,它提供了对元素的唯一性排序。TreeSet中的元素是唯一的,并且按照升序排序。

        TreeMap也是基于红黑树实现的,它提供了一个键值对的映射关系,并且按照键的升序排序。TreeMap允许使用null键和null值。

        这两个类都实现了NavigableMap和SortedMap接口,提供了更丰富的方法用于搜索、排序和遍历操作。

        为了更好的理解TreeSet和TreeMap的使用以及底层原理,下面带大家模拟实现一下搜索树。

2、模拟实现

2.1、查找

 若根节点不为空:

  • 如果根节点key==查找key,返回true
  • 如果根节点key > 查找key,去其左子树上查找
  • 如果根节点key < 查找key,去其右子树上查找

否则返回false

    public boolean search(int val) {TreeNode cur = root;while (cur != null) {if (cur.val > val) {   //当前节点大于val,去左子树上继续找cur = cur.left;} else if (cur.val < val) {   //当前节点小于val,去右子树上继续找cur = cur.right;} else {return true;   //找到返回true}}return false;}

2.2、插入

插入时分为两种大情况:树为空以及树不为空

1、如果树为空树,即根 == null,直接插入

2、如果树不是空树,按照查找逻辑确定插入位置,插入新结点

    public void insert(int val) {if (root == null) {    //树为空时直接插入并返回root = new TreeNode(val);return;}TreeNode cur = root;TreeNode prev = null;    //指向cur前一个节点(即父亲节点),用于最终插入时使用while (cur != null) {prev = cur;      //更新prev指向if (cur.val > val) {cur = cur.left;} else if (cur.val < val) {cur = cur.right;} else {return;  //遇到相同退出,无需重复插入}}if (prev.val > val) {    //判断插入的正确位置prev.left = new TreeNode(val);} else {prev.right = new TreeNode(val);}}

2.3、删除(难点)

删除操作是二叉搜索树三种基本操作中最难的一个操作,它的难点不在于代码的难度,而是因为涉及到的情况居多,稍微不注意就容易漏判断某一条情况,因此使用合理的思路去理解记忆二叉搜索树的删除操作是非常必要的。

 设待删除结点为 cur, 待删除结点的双亲结点为 prev。对cur删除节点的三种情况进行分析:

1. cur.left == null

  • cur 是 root,则 root = cur.right
  • cur 不是 root,cur 是 prev.left,则 prev.left = cur.right
  • cur 不是 root,cur 是 prev.right,则 prev.right = cur.right

2. cur.right == null

  • cur 是 root,则 root = cur.left
  • cur 不是 root,cur 是 prev.left,则 prev.left = cur.left
  • cur 不是 root,cur 是 prev.right,则 prev.right = cur.left

3. cur.left != null && cur.right != null

需要使用替换法进行删除。

替换法的核心是:找到删除节点左子树中的最大值(即子树中的最右节点,这个节点一定没有右右子树),或者删除节点右子树中的最小值(即子树中的最左节点,这个节点一定没有左子树),用它的值填补到被删除节点中,再来处理该结点的删除问题。

以下讲解使用替换删除节点右子树的最小节点:

首先找到最小节点tmp,以及最小节点的父亲tmpPrev,如图所示结构,紧着cur.val = tmp.val替换值,然后删除tmp节点,删除步骤如图所示,即tmpPrev.left = tmp.right。

值得注意的是,有一种情况会被忽略,即当cur.right 节点无左子树时,此时tmpPrev仍然是cur,而tmp即为cur.right,此时删除tmp节点的步骤就变为了tmpPrev.right = tmp.right 

 

    public void remove(int val) {TreeNode cur = root;TreeNode prev = null;while (cur != null) {prev = cur;if(cur.val > val) {cur = cur.left;} else if (cur.val < val){cur = cur.right;} else {removeNode(cur,prev);return;}}}public void removeNode(TreeNode cur, TreeNode prev) {if(cur.left == null) {if(cur == root) {root = cur.right;} else if (prev.left == cur) {prev.left = cur.right;} else {prev.right = cur.right;}} else if (cur.right == null) {if(cur == root) {root = cur.left;} else if (prev.left == cur) {prev.left = cur.left;} else {prev.right = cur.left;}} else {TreeNode tmp = cur.right;TreeNode tmpPrev = cur;while(tmp.left != null) {tmpPrev = tmp;tmp = tmp.left;}cur.val = tmp.val;//注意,可能没有进入while循环,此时这里的两种情况if (tmpPrev.left == tmp) {tmpPrev.left = tmp.right;} else {tmpPrev.right = tmp.right;}}}

3、性能分析

  • 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
  • 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
  • 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

 

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:\log_{2}N

最差情况下,二叉搜索树退化为单支树,其平均比较次数为:\frac{N}{2}

如果退化成单支树,二叉搜索树的性能就失去了,而为了改进这一缺陷,就有了AVL树。

AVL树是最先发明的自平衡二叉搜索树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

本篇博客的重点不在AVL树上,因此不对AVL树进行展开讲解,可自行搜索了解。 

 4、完整代码

package BinarySearchTree;public class BinarySearchTree {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 val) {TreeNode cur = root;while (cur != null) {if (cur.val > val) {cur = cur.left;} else if (cur.val < val) {cur = cur.right;} else {return true;}}return false;}public void insert(int val) {if (root == null) {root = new TreeNode(val);return;}TreeNode cur = root;TreeNode prev = null;while (cur != null) {prev = cur;if (cur.val > val) {cur = cur.left;} else if (cur.val < val) {cur = cur.right;} else {return;  //相同退出}}if (prev.val > val) {prev.left = new TreeNode(val);} else {prev.right = new TreeNode(val);}}public void remove(int val) {TreeNode cur = root;TreeNode prev = null;while (cur != null) {prev = cur;if(cur.val > val) {cur = cur.left;} else if (cur.val < val){cur = cur.right;} else {removeNode(cur,prev);return;}}}public void removeNode(TreeNode cur, TreeNode prev) {if(cur.left == null) {if(cur == root) {root = cur.right;} else if (prev.left == cur) {prev.left = cur.right;} else {prev.right = cur.right;}} else if (cur.right == null) {if(cur == root) {root = cur.left;} else if (prev.left == cur) {prev.left = cur.left;} else {prev.right = cur.left;}} else {TreeNode tmp = cur.right;TreeNode tmpPrev = cur;while(tmp.left != null) {tmpPrev = tmp;tmp = tmp.left;}cur.val = tmp.val;//注意当没有进入while循环时这里的两种情况if (tmpPrev.left == tmp) {tmpPrev.left = tmp.right;} else {tmpPrev.right = tmp.right;}}}
}

 【博主推荐】

【LeetCode力扣】面试题 17.14. 最小K个数(top-k问题)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/135737266?spm=1001.2014.3001.5501【LeetCode力扣】42. 接雨水-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/134104222?spm=1001.2014.3001.5501【LeetCode力扣】189 53 轮转数组 | 最大子数组和-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/134095703?spm=1001.2014.3001.5501

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!

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

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

相关文章

用通俗易懂的方式讲解:大模型 RAG 高阶技巧,如何实现窗口上下文检索

在本文中&#xff0c;我们将介绍一种提高RAG&#xff08;Retrieval-Augmented Generation&#xff09;模型检索效果的高阶技巧&#xff0c;即窗口上下文检索。 我们将首先回顾一下基础RAG的检索流程和存在的问题&#xff0c;然后介绍窗口上下文检索的原理和实现方法&#xff0…

【MATLAB源码-第124期】基于matlab的GFDM系统(64QAM/QPSK调制)在AWGN和PA信道误码率对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 广义频分复用&#xff08;GFDM&#xff09;是一种先进的信号调制技术&#xff0c;近年来在无线通信领域获得了广泛的关注。GFDM作为一种多载波调制方案&#xff0c;是对经典的正交频分复用&#xff08;OFDM&#xff09;技术的…

Stable Diffusion系列(四):提示词规则与使用

文章目录 基础规则高级规则插件使用基于相机镜头增强提示词常用提示词总结奇特提示词珍藏 基础规则 所谓提示词&#xff0c;也就是文生图中的文&#xff0c;由连贯的英语单词或句子组成。其最基础的规则是&#xff1a; 不同提示词之间需要用英文逗号分隔&#xff0c;空格和换…

【极数系列】Flink环境搭建Linux版本 (03)

文章目录 引言01 Linux部署JDK11版本1.下载Linux版本的JDK112.创建目录3.上传并解压4.配置环境变量5.刷新环境变量6.检查jdk安装是否成功 02 Linux部署Flink1.18.0版本1.下载Flink1.18.0版本包2.上传压缩包到服务器3.修改flink-config.yaml配置4.启动服务5.浏览器访问6.停止服务…

【第七天】蓝桥杯备战

题 1、最大距离2、最长递增 1、最大距离 https://www.lanqiao.cn/problems/155/learning/ 解法&#xff1a;暴力遍历 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan …

第九节HarmonyOS 常用基础组件16-Blank

1、描述 空白填充组件&#xff0c;在容器主轴方向上&#xff0c;空白填充组件具有自动填充容器空余部分的能力。仅当父组件为Row/Column/Flex时生效。 2、接口 Blank(min?: number | string) 3、参数 参数名 参数类型 必填 描述 min number|string 否 空白填充组件…

2.3_8 多生产者-多消费者问题

2.3_8 多生产者-多消费者问题 实现思路 semaphore mutex1; //实现互斥访问盘子(缓冲区) semaphore apple0; //盘子中有几个苹果 semaphore orange0; //盘子中有几个橘子 semaphore plate 1; //盘子中还可以放多少个水果dad(){while(1){准备一个苹果;P(plate);P(mutex);把苹果放…

GMS测试BTSfail-CVE-2022-20451

描述&#xff1a; 项目需要过GMS兼容性测试&#xff0c;BTS这块我们环境没有&#xff0c;送检之后出现了一个BTS的Alert&#xff0c;这个是必须要解决的。下面的warning可以不考虑。 这个是patch问题&#xff0c;根据代理提供的pdf文件找到一个id:为A-235098883的补丁&#xf…

数据库管理-第141期 DG PDB - Oracle DB 23c(20240129)

数据库管理141期 2024-01-29 第141期 DG PDB - Oracle DB 23c&#xff08;20240129&#xff09;1 概念2 环境说明3 操作3.1 数据库配置3.2 配置tnsname3.3 配置强制日志3.4 DG配置3.5 DG配置建立联系3.6 启用所有DG配置3.7 启用DG PDB3.8 创建源PDB的DG配置3.9 拷贝pdbprod1文件…

UDP通信以及本地套接字

1. UDP 1.1 UDP 通信&#xff1a; UDP服务端创建出来的套接字不是监听套接字&#xff0c;直接就是通信套接字。 #include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *de…

源码安装nginx并提供服务脚本

一、下载nginx ①官网复制下载链接 ②在Linux中下载 [rootopenEuler2 ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz 二、解压并指定路径 [rootopenEuler2 ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/ 三、安装依赖 dnf install -y gcc gcc-c mak…

备战蓝桥杯----贪心算法(二进制)

已经差不多掌握了贪心的基本思想&#xff0c;让我们看几道比较趣的题吧&#xff01; 先来个比较有意思的题热热身&#xff1a; 法1.我们可以先把l,r化成二进制的形式。 然后分俩种情况&#xff1a; &#xff08;1&#xff09;若他们位数不一样并且位数高的全为1&#xff0c;…