二叉搜索树、二叉排序树(查找、插入和删除)——Java版本

1. 概念

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

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

如:一个int a [] = {5,3,4,1,7,8,2,6,0,9}; 数组组成二叉排序树

定义二叉搜索树的结构:

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

2. 操作 - 查找 

    /*搜索搜索的时间复杂度最好:O(logN)搜索的时间复杂度最坏:O(N)*/public boolean search(int key){TreeNode cur = root;while(cur != null){//找到key,返回trueif(cur.val == key) {return true;}//到右树寻找else if(cur.val < key){cur = cur.right;}//到左树寻找else{cur = cur.left;}}//没有找到值为key的结点return false;}

 

3. 操作 - 插入

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

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

    /*插入:都是插入到叶子结点插入的时间复杂度最好:O(logN)插入的时间复杂度最坏:O(N)*/public void insert(int val) throws Exception {TreeNode newNode = new TreeNode(val);if (root == null) {root = newNode;return;}TreeNode cur = root;TreeNode parent = null;while (cur != null) {parent = cur;//判断是否有重复元素进入if (cur.val == val) {throw new Exception("有重复元素进入!");}//如果要插入的结点的值,大于当前结点,就应该在cur的右子树if (cur.val < val) {cur = cur.right;}//如果要插入的结点的值,小于当前结点,就应该在cur的左子树else if (cur.val > val) {cur = cur.left;}}//如果要插入的结点小于父节点,就应该接在父节点的左子树if (parent.val > val) {parent.left = newNode;}//如果要插入的结点大于父节点,就应该接在父节点的右子树else {parent.right = newNode;}}

搜索和插入的运行结果: 

4. 操作 - 删除 ⭐⭐⭐⭐⭐

分情况讨论,如下所示: 

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

(1)cur.left == null

① cur 是 root,则 root = cur.right

② cur 不是 root,cur 是 parent.left,则 parent.left = cur.right

 ③ cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

 (2)cur.right == null

① cur 是 root,则 root = cur.left

② cur 不是 root,cur 是 parent.left,则 parent.left = cur.left 

③ cur 不是 root,cur 是 parent.right,则 parent.right = cur.left  

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

需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被 删除节点中,再来处理该结点的删除问题。

 这部分的代码为(有bug):

        //3.cur两边的子树都不为空else{TreeNode t = cur.left;TreeNode tp = null;while(t.right != null){tp = t;t = t.right;}cur.val = t.val;//这个就变成了删除t指向的结点,且t结点的右子树为空tp.right = t.left;}

原来的图: 

第一步进行分析和改进:

第二步进行分析和改进:

树类的方法: 

    //删除结点的操作public void remove(int val) {TreeNode parent = null;TreeNode cur = root;while (cur != null) {//去右子树寻找if (cur.val < val) {parent = cur;cur = cur.right;} else if (cur.val > val) {parent = cur;cur = cur.left;}//找到了else {removeNode(cur, parent);return;}}}private void removeNode(TreeNode cur, TreeNode parent) {//1.cur的左子树为空if (cur.left == null) {//(1)cur 是 root,则 root = cur.rightif (cur == root) {root = cur.right;}//(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.rightelse if (cur == parent.left) {parent.left = cur.right;}//(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.rightelse {parent.right = cur.right;}}//2.cur的右子树为空else if (cur.right == null) {//(1)cur 是 root,则 root = cur.leftif (cur == root) {root = cur.left;}//(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.leftelse if (cur == parent.left) {parent.left = cur.right;}//(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.leftelse {parent.right = cur.right;}}//3.cur两边的子树都不为空else {TreeNode t = cur.left;TreeNode tp = cur;while (t.right != null) {tp = t;t = t.right;}cur.val = t.val;if (cur == tp) {tp.left = t.left;} else {//这个就变成了删除t指向的结点,且t结点的右子树为空tp.right = t.left;}}}

 测试类:

public class Test {public static void main(String[] args) throws Exception {BinarySearchTree binarySearchTree = new BinarySearchTree();binarySearchTree.insert(5);binarySearchTree.insert(3);binarySearchTree.insert(7);binarySearchTree.insert(1);binarySearchTree.insert(4);binarySearchTree.insert(6);binarySearchTree.insert(8);binarySearchTree.insert(0);binarySearchTree.insert(9);binarySearchTree.remove(3);}
}

5. 二叉搜索树的完整代码

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;/*搜索搜索的时间复杂度最好:O(logN)搜索的时间复杂度最坏:O(N)*/public boolean search(int key) {TreeNode cur = root;while (cur != null) {//找到key,返回trueif (cur.val == key) {return true;}//到右树寻找else if (cur.val < key) {cur = cur.right;}//到左树寻找else {cur = cur.left;}}//没有找到值为key的结点return false;}/*插入:都是插入到叶子结点插入的时间复杂度最好:O(logN)插入的时间复杂度最坏:O(N)*/public void insert(int val) throws Exception {TreeNode newNode = new TreeNode(val);if (root == null) {root = newNode;return;}TreeNode cur = root;TreeNode parent = null;while (cur != null) {parent = cur;//判断是否有重复元素进入if (cur.val == val) {throw new Exception("有重复元素进入!");}//如果要插入的结点的值,大于当前结点,就应该在cur的右子树if (cur.val < val) {cur = cur.right;}//如果要插入的结点的值,小于当前结点,就应该在cur的左子树else if (cur.val > val) {cur = cur.left;}}//如果要插入的结点小于父节点,就应该接在父节点的左子树if (parent.val > val) {parent.left = newNode;}//如果要插入的结点大于父节点,就应该接在父节点的右子树else {parent.right = newNode;}}//删除结点的操作public void remove(int val) {TreeNode parent = null;TreeNode cur = root;while (cur != null) {//去右子树寻找if (cur.val < val) {parent = cur;cur = cur.right;} else if (cur.val > val) {parent = cur;cur = cur.left;}//找到了else {removeNode(cur, parent);return;}}}private void removeNode(TreeNode cur, TreeNode parent) {//1.cur的左子树为空if (cur.left == null) {//(1)cur 是 root,则 root = cur.rightif (cur == root) {root = cur.right;}//(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.rightelse if (cur == parent.left) {parent.left = cur.right;}//(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.rightelse {parent.right = cur.right;}}//2.cur的右子树为空else if (cur.right == null) {//(1)cur 是 root,则 root = cur.leftif (cur == root) {root = cur.left;}//(2)cur 不是 root,cur 是 parent.left,则 parent.left = cur.leftelse if (cur == parent.left) {parent.left = cur.right;}//(3)cur 不是 root,cur 是 parent.right,则 parent.right = cur.leftelse {parent.right = cur.right;}}//3.cur两边的子树都不为空else {TreeNode t = cur.left;TreeNode tp = cur;while (t.right != null) {tp = t;t = t.right;}cur.val = t.val;if (cur == tp) {tp.left = t.left;} else {//这个就变成了删除t指向的结点,且t结点的右子树为空tp.right = t.left;}}}}

 

6. 性能分析

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

  • 最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:\log N
  • 最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以,是二叉搜索树的性能最佳? 

答:可以,这就涉及到后面的AVL树和红黑树了,后期的文章会继续讨论AVL树和红黑树。

7. 和Java类集的关系

TreeMap 和 TreeSet 即 Java 中利用搜索树实现的 Map 和 Set;实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础之上 + 颜色以及红黑树性质验证,关于红黑树的内容后序再进行讲解。

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

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

相关文章

Rsync服务

一、Rsync概述 rsync英文称为 remote synchronizetion&#xff0c;rsync具有可使本地和远程两台主机之间的数据快速复制同步镜像、远程备份的功能&#xff0c;功能类似于ssh带的scp命令&#xff0c;优于scp命令的功能&#xff0c;scp每次都是全量拷贝&#xff0c;而rsync可以增…

Rust Web小项目

Rust 第26节 Web小项目 监听TCP链接 use std::net::TcpListener;fn main() {let listener TcpListener::bind("127.0.0.1:7887").unwrap(); //监听7887端口&#xff0c;成功后&#xff0c;就创建一个linstenerfor stream in listener.incoming() { // listener.…

2024年mongodb自建三节点副本集详细教程

环境说明 系统centos7.9 自建服务器或云服务器&#xff0c;硬件要求不低于2核2G内存&#xff0c;20G硬盘&#xff0c;文件系统默认是ext4即可。 生产环境最好单独一个磁盘存放数据库&#xff0c;方便数据备份和还原&#xff0c;避免干扰到其他磁盘的运作。 mongodb 4.4.27 …

HTML-表格

表格 1.基本结构 一个完整的表格由&#xff1a;表格标题、表格头部、表格主体、表格脚注&#xff0c;四部分组成 表格涉及到的标签&#xff1a; table&#xff1a;表格 caption&#xff1a;标题 thead&#xff1a;表格头部 tbody&#xff1a;表格主体 tfoot&#xff1a;表格注…

android:persistent和android:priority的区别,对进程优先级有什么影响?

前言&#xff1a;写的apk因为系统busy给我kill了&#xff0c;(adj 900): kill all background&#xff0c;在AndroidManifest.xml添加android:persistent"true"后&#xff0c;被甲方要求不能这样做&#xff0c;还是得从adj改&#xff0c;把 priority改成1000 android…

可Pin to Pin兼容DRV8837的国产H桥电机驱动芯片,具大电流,短gnd,短电源保护功能

在国产牙刷&#xff0c;电子锁设计中&#xff0c;以前方案很多采用TI的DRV8837做直流电机驱动&#xff0c;随着中美贸易战和牙刷&#xff0c;电子锁等产品价格平民化普及&#xff0c;很多大厂在做国产化替代设计方案&#xff0c;GLOBALCHIP 的电机驱动芯片GC8837&#xff0c;价…

解读Android进程优先级ADJ算法

本文基于原生Android 9.0源码来解读进程优先级原理,基于篇幅考虑会精炼部分代码 一、概述 1.1 进程 Android框架对进程创建与管理进行了封装,对于APP开发者只需知道Android四大组件的使用。当Activity, Service, ContentProvider, BroadcastReceiver任一组件启动时,当其所…

YOLOv8改进 | Conv篇 | 2024.1月最新成果可变形卷积DCNv4(适用检测、Seg、分类、Pose、OBB)

一、本文介绍 本文给大家带来的改进机制是2024-1月的最新成果DCNv4,其是DCNv3的升级版本,效果可以说是在目前的卷积中名列前茅了,同时该卷积具有轻量化的效果!一个DCNv4参数量下降越15Wparameters左右,。它主要通过两个方面对前一版本DCNv3进行改进:首先,它移除了空间聚…

Python 流静态文件过滤、端口过滤、同域过滤(host过滤)、代理拦截

目录 静态文件过滤 需求 代码 端口过滤 需求 代码 同域过滤&#xff08;host过滤&#xff09; 需求 代码 静态文件过滤 需求 流量中的url包含大量静态文件请求信息&#xff0c;过滤掉 代码 def __is_static(self, flow: http.HTTPFlow) -> bool:static_ext [.j…

探讨Go语言中的HTTP代理模式:看Go如何玩转网络中转站

在互联网的海洋中&#xff0c;HTTP代理服务器像一座灯塔&#xff0c;为我们的网络冲浪提供了指引。而当Go语言遇上HTTP代理&#xff0c;会碰撞出怎样的火花呢&#xff1f;今天&#xff0c;让我们一起探讨Go语言中的HTTP代理模式&#xff0c;看看它如何玩转这个网络中转站&#…

三:C语言-输入与输出

三&#xff1a;输入与输出 一&#xff1a;输出 1.printf()&#xff1a; ​ 将参数文本输出到屏幕上&#xff0c;它名字里的 f 代表 format&#xff08;格式化&#xff09;&#xff0c;表示可以定制输出文本的格式 ​ printf()不会在行尾自动添加换行符&#xff0c;待运行结…

IDEA(十)2022版本 Services中服务窗口不显示端口号解决

目录 一、问题描述二、问题分析三、解决方案3.1 设置启动参数【生效】3.2 方法二&#xff1a;设置环境变量【不生效】3.3 方法三&#xff1a;删除缓存【不生效】 四、补充&#xff1a;如何手动控制端口显示 一、问题描述 我们在使用 IDEA 的过程中&#xff0c;会发现在 Servic…

Hive之set参数大全-11

设置 Map Join 操作中优化哈希表的工作集大小&#xff08;working set size&#xff09; hive.mapjoin.optimized.hashtable.wbsize 是 Apache Hive 中的一个配置属性&#xff0c;用于设置 Map Join 操作中优化哈希表的工作集大小&#xff08;working set size&#xff09;。 …

Dockerfile:如何写一个Dockerfile文件?

如何写一个Dockerfile文件&#xff1f; &#x1f6a8;推荐参考&#xff1a;Dockerfile&#xff1a;如何写一个Dockerfile文件&#xff1f; 现在的项目肯定都离不开docker&#xff0c;只要是流水线部署就会涉及Dockerfile文件&#xff0c;那么如何写一个正确的编写一个Dockerfil…

dpdk网络转发环境的搭建

文章目录 前言ip命令的使用配置dpdk-basicfwd需要的网络结构测试dpdk-basicfwddpdk-basicfwd代码分析附录basicfwd在tcp转发时的失败抓包信息DPDK的相关设置 前言 上手dpdk有两难。其一为环境搭建。被绑定之后的网卡没有IP&#xff0c;我如何给它发送数据呢&#xff1f;当然&a…

[leetcode] 18. 四数之和

文章目录 题目描述解题方法排序 双指针java代码 相似题目 题目描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&a…

二、Kotlin 内置类型

1. 基本类型 1.1 Kotlin 和 Java 的基本类型对比 KotlinJava字节Bytebyte/Byte整型Int & Longint/Integer & long/Long浮点型Float & Doublefloat/Float & double/Double字符Charchar/Chararcter字符串StringString 1.2 定义变量 1.2.1 val 只读变量 &…

qt初入门5:字体设置和元对象系统的练习

空闲时间&#xff0c;参考课本demo&#xff0c;做一下练习。 字体的颜色主要用QPalette类&#xff0c;调色板的作用&#xff0c;控制着窗口部件的颜色和外观&#xff0c;包括背景色、前景色、文本颜色、边框颜色等。 字体的显示样式主要用Font类&#xff0c;用于管理字体。 元…

python三数之和

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 1…

无限学模式-“重塑科研学习路径:ChatGPT应用实战课,开启高效率、高创新的科研之旅!“

ChatGPT 在论文写作与编程方面也具备强大的能力。无论是进行代码生成、错误调试还是解决编程难题&#xff0c;ChatGPT都能为您提供实用且高质量的建议和指导&#xff0c;提高编程效率和准确性。此外&#xff0c;ChatGPT是一位出色的合作伙伴&#xff0c;可以为您提供论文写作的…