二叉搜索树、二叉排序树(查找、插入和删除)——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,一经查实,立即删除!

相关文章

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进行改进:首先,它移除了空间聚…

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

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

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

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

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;用于管理字体。 元…

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

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

【研0日记】24.01.25

回家倒数第6天 受不了了&#xff0c;不想写了&#xff0c;这群b怎么这么能写 用latex写了个伪代码&#xff0c;有点好玩 \usepackage[ruled,linesnumbered]{algorithm2e} \begin{algorithm}[ht] \caption{Pipeline of Kernel Iteration in K-Net.} \label{alg:alg1} …

在Java中如何优雅使用正则表达式?

在Java中如何优雅使用正则表达式&#xff1f; 一、正则表达式的基本概念与用途 1.1 正则表达式的简介 正则表达式&#xff0c;又称规则表达式。&#xff08;英语&#xff1a;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是计算…

深入理解badblocks

文章目录 一、概述二、安装2.1、源码编译安装2.2、命令行安装2.3、安装确认 三、重要参数详解3.1、查询支持的参数3.2、参数说明 四、实例4.1、全面扫描4.2、破坏性写入并修复4.3、非破坏性写入测试 五、实现原理六、注意事项 团队博客: 汽车电子社区 一、概述 badblocks命令是…

代码随想录算法训练营第十六天|104.二叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

104.二叉树的最大深度 思路&#xff1a;这道题最开始的时候&#xff0c;我想的是用前序遍历的思路来做&#xff0c;整个过程有剪枝的过程&#xff0c;弄了半天没写出来&#xff0c;主要是剪枝没写对&#xff01;最大深度是叶子节点的高度&#xff0c;可以使用后序遍历来做。 cl…

el-table 动态渲染多级表头;一级表头根据数据动态生成,二级表头固定

一、表格需求&#xff1a; 实现一个动态表头&#xff0c;一级表头&#xff0c;根据数据动态生成&#xff0c;二级表头固定&#xff0c;每列的数据不一样&#xff0c;难点在于数据的处理。做这种表头需要两组数据&#xff0c;一组数据是实现表头的&#xff0c;另一组数据是内容…

Web网页生成桌面应用

前言&#xff1a;网页生成桌面指的是将一个网页保存为桌面应用程序的形式&#xff0c;使得用户可以在桌面上直接打开该网页&#xff0c;而不必通过浏览器打开。这种桌面应用程序一般具有独立的窗口、菜单、工具栏等界面元素&#xff0c;能够提供更加方便快捷的使用体验。 实现…

【pytorch】pytorch学习笔记(续1)

p22&#xff1a;1.加减乘除&#xff1a; &#xff08;1&#xff09;add(a,b)&#xff1a;等同于ab。 &#xff08;2&#xff09;sub(a,b)&#xff1a;等同于a-b。 &#xff08;3&#xff09;mul(a,b)&#xff1a;等同于a*b。 &#xff08;4&#xff09;div(a,b)&#xff1a…