【数据结构】图解二叉搜索树的新增、搜索、删除

一、概念

二叉搜索树(Binary Search Tree,简称BST)也称为二叉查找树或二叉排序树,是一种具有特殊性质的二叉树数据结构

  1. 定义和性质
  • 二叉搜索树中的每个节点包含一个键值,习惯上我们说左子树上所有节点的键值均小于其根节点的键值,右子树上所有节点的键值均大于其根节点的键值。
  • 二叉搜索树的左右子树也都是二叉搜索树。
  • 它能够以 O(\log n) 的时间复杂度进行插入、删除和查找操作。
  1. 基本操作
  • 插入:将一个新值按照二叉搜索树的性质插入到适当的位置。
  • 删除:从树中移除一个值,同时保证二叉搜索树的性质不被破坏。
  • 查找:检查树中是否存在某个特定的值。
  • 查询排名:查找比给定值小的数的个数加一(即该值在有序序列中的位置)。
  • 查询指定排名的元素:找到有序序列中特定位置的元素。
  • 求前驱和后继:分别找到小于某值的最大数和大于某值的最小数。
  1. 优势
  • 相比于数组,二叉搜索树提供了更快的插入和删除操作,因为数组的插入和删除操作需要移动大量元素来维护有序性,时间复杂度为O(n);而链表虽然可以快速插入和删除,但是查找等其他操作的时间复杂度不够优秀。
  • 二叉搜索树结合了两者的优点,对于上述每个操作都拥有较好的时间复杂度。

综上所述,二叉搜索树是计算机科学中一种非常重要的数据结构,它不仅提供了高效的数据检索功能,还允许数据的动态插入和删除,因此在数据库索引、内存管理等领域得到了广泛的应用。

二、图解
1.查找

有如下这样一颗二叉树,我们需要查询9所在节点

这时根节点值为7比目标值小,所以我们一应该去右子树中寻找

当前cur节点的值依旧比目标值9要小,于是我们又去他的右子树中寻找

此时cur节点值比目标值大,我们就可以去他的左边去寻找

此时相等结束寻找

2.插入

首先我们分别将21、1、15、10分别插入这颗二叉搜索树

首先我们需要找到合适的位置,定义一个指针从根节点开始寻找,先模拟插入1

此时值比待插入节点值大,于是去遍历左子树寻找合适的位置

还是比待插入节点大,再次往左走

依旧往左发现往左为空,于是我们就可以将1插入到此处,但是要想插入到这里,我们需要记录父亲节点,所以在遍历的时候我们需要记录父亲节点位置

重复上述步骤插入10

我们发现每次插入都落在了叶子节点上

所以我们只需要找到插入位置的父亲节点即可

 3.删除

二叉搜索树的删除操作主要涉及以下几种情况:

  1. 删除叶子节点:如果待删除的节点是叶子节点,即没有子节点,可以直接删除该节点。
  2. 删除只有左子树的节点:如果待删除的节点只有一个左子节点,可以用其左子节点替代待删除节点的位置。
  3. 删除只有右子树的节点:如果待删除的节点只有一个右子节点,可以用其右子节点替代待删除节点的位置。
  4. 删除左右子树均不空的节点:这种情况最为复杂。通常的做法是找到该节点的右子树中的最小节点(或者左子树中的最大节点),用这个节点的值替换待删除节点的值,然后删除那个最小(或最大)节点。

具体步骤如下:

  1. 查找节点:首先需要找到要删除的节点。这通常是通过递归搜索完成的,比较待删除值与当前节点值的大小,然后决定是向左子树还是右子树继续搜索。
  2. 替换节点:一旦找到了要删除的节点,根据上述情况采取相应的替换策略。如果是第四种情况,需要找到合适的替换节点并进行值的交换。
  3. 维护BST性质:在删除节点后,需要确保树仍然保持二叉搜索树的性质,即任何节点的值都大于其左子树中的所有值,小于其右子树中的所有值。
  4. 处理替换节点的子树:如果替换节点有子节点,需要将其子节点接到被替换节点的相应位置,以保持树的结构完整性。

总的来说,二叉搜索树的删除操作是一个相对复杂的过程,需要根据不同的情况采取不同的策略,并且在整个过程中保持树的平衡和有序性。

下面将图解上述三种情况:

一、首先是待删除的节点没有左节点:

没有左节点将会分为以下三种情况:下图中节点值未按照二叉搜索树规则,注意节点位置即可其中值可忽略

1.待删除节点是根节点

只需要让根节点root指向root.right即可

2.待删除节点是父亲节点的左孩子

我们只需让parent.left=cur.right。

3.待删除节点是父亲节点右孩子

只需要让parent.right=cur.right

二、待删除节点没有右节点

1.待删除节点是根节点:

这个时候只需让root = root.left即可

2.待删除节点是父亲节点的左孩子

这个时候只需要让parent.left=cur.left

3.待删节点是父亲节点的右孩子

只需要让parent.right=cur.left

三、待删除节点有两个孩子

这种情况最为复杂。通常的做法是找到该节点的右子树中的最小节点(或者左子树中的最大节点),用这个节点的值替换待删除节点的值,然后删除那个最小(或最大)节点。

首先我们可以在待删除节点位置开始去他的左子树中寻找最大值(左子树中都比当前节点值小)然后进行替换,或者去右子树中寻找最小值进行替换

我们只需要在左子树中寻找到最大值,然后进行替换将左子树中最大值删掉即可

三、代码实现
import java.util.LinkedList;
import java.util.Queue;

public class BinaryTree extends 余胜军{
    static class TreeNode extends 余胜军 {
        int val;
        TreeNode left;
        TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }

    private TreeNode root;

    public TreeNode 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 cur;
            }
        }
        return null;
    }

    public void display(TreeNode root) {
        if (root == null) return;
        display(root.left);
        System.out.print(root.val + " ");
        display(root.right);
    }

    public boolean insert(int val) {
        TreeNode pre = null;
        TreeNode cur = root;

        if (cur == null) {
            root = new TreeNode(val);
            return true;
        }

        while (cur != null) {
            if (cur.val < val) {
                pre = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                pre = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }

        if (pre.val > val) pre.left = new TreeNode(val);
        else pre.right = new TreeNode(val);
        return true;
    }

    public boolean remove(int key) {
        TreeNode pre = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < key) {
                pre = cur;
                cur = cur.right;
            } else if (cur.val > key) {
                pre = cur;
                cur = cur.left;
            } else {
                // 开始删除
                return removeNode(pre, cur);
            }
        }
        return false;
    }

    public void level() {
        if (root == null) System.out.println("tree is null");
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size-- != 0) {
                TreeNode top = queue.poll();
                System.out.print(top.val + " ");
                if (top.left != null) queue.offer(top.left);
                if (top.right != null) queue.offer(top.right);
            }
            System.out.println();
        }
    }

    private boolean removeNode(TreeNode pre, TreeNode cur) {
        if (cur.left == null) { // 左为空
            if (cur == root) {
                root = root.right;
            } else if (cur == pre.left) {
                pre.left = cur.right;
            } else {
                pre.right = cur.right;
            }
        } else if (cur.right == null) { // 右为空
            if (cur == root) {
                root = root.left;
            } else if (cur == pre.left) {
                pre.left = cur.left;
            } else {
                pre.right = cur.left;
            }
        } else {  // 左右都为空
            // 寻找左左子树的最大值
            TreeNode targetParent = cur;
            TreeNode target = cur.left;
            while (target.right != null) {
                targetParent = target;
                target = target.right;
            }
            // 替换
            cur.val = target.val;
            if (targetParent.left == target) targetParent.left = target.left;
            else targetParent.right = target.left;
//            TreeNode tp = cur;
//            TreeNode t = cur.right;
//            while (t.left != null) {
//                tp = t;
//                t = t.left;
//            }
//
//            cur.val = t.val;
//            if (tp.left == t) tp.left = t.left;
//            else tp.right = t.right;
        }
        return true;
    }

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.insert(80);
        tree.insert(30);
        tree.insert(48);
        tree.insert(60);
        tree.insert(90);
        tree.insert(56);
        tree.display(tree.root);
        System.out.println();
        tree.remove(80);
        tree.display(tree.root);
    }
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

乔琼:高性能会议传声器的产品优化设计| 演讲嘉宾公布

一、智能家居与会议系统 智能家居与会议系统分论坛将于3月28日同期举办&#xff01; 智能会议系统它通过先进的技术手段&#xff0c;提高了会议效率&#xff0c;降低了沟通成本&#xff0c;提升了参会者的会议体验。对于现代企业、政府机构和学术界是不可或缺的。在这里&#x…

部署运维 防火墙,进程 常用命令

防火墙: 1. 查看是否安装了firewalld sudo systemctl status firewalld 查看防火墙状态或者sudo systemctl is-active firewalld 查看防火墙是否是开启状态 2. 开放6379port sudo firewall-cmd --add-port6379/tcp --permanent 刷新防火墙 sudo firewall-cmd --reload 3…

MinIO Client(mc)基本使用

一、Linux安装 1、下载最近的mc 命令&#xff0c;并保存到当前用户的bin目录下&#xff0c;这样可以直接执行&#xff0c;不用修改path curl -sL https://dl.minio.org.cn/client/mc/release/linux-amd64/mc -o /usr/bin/mc chmod x /usr/bin/mc二、配置使用 1、查看mc已经…

【洛谷 P8748】[蓝桥杯 2021 省 B] 时间显示 题解(数学+模运算+输入输出)

[蓝桥杯 2021 省 B] 时间显示 题目描述 小蓝要和朋友合作开发一个时间显示的网站。在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表示&#xff0c;值为从 1970 年 1 月 1 日 00:00:00 到当前时刻经过的毫秒数。 现在&#xff0c;小蓝要在客户端显示…

MATLAB KL变换

1. 原理 KL变换步骤&#xff1a; 1.求样本X的协方差矩阵R 2.求 R的特征值λ。选取前d个较大的特征值。 3.计算d个特征值对应的特征向量&#xff0c;归一化后构成变换矩阵U。 4.对{X}中每一个X进行K-L变换&#xff0c;得到变换后向量YU’ * X&#xff0c;d维向量Y就是…

阿里云Linux系统MySQL8忘记密码修改密码

相关版本 操作系统&#xff1a;Alibaba Cloud Linux 3.2104 LTS 64位MySQL&#xff1a;mysql Ver 8.0.34 for Linux on x86_64 (Source distribution) MySQL版本可通过下方命令查询 mysql --version一、修改my.cnf文件 文件位置&#xff1a;etc/my.cnf进入远程连接后可以打…

解决vue项目本地开发完成后部署到服务器后报404的问题

一、如何部署 前后端分离开发模式下&#xff0c;前后端是独立布署的&#xff0c;前端只需要将最后的构建物上传至目标服务器的web容器指定的静态目录下即可 我们知道vue项目在构建后&#xff0c;是生成一系列的静态文件 常规布署我们只需要将这个目录上传至目标服务器即可 /…

163邮箱SMTP设置需要怎么配置?如何开启?

163邮箱SMTP设置教程&#xff1f;163邮箱开通SMTP发信功能方法&#xff1f; 当我们需要使用第三方工具或软件来发送邮件时&#xff0c;SMTP设置就显得尤为重要。特别是对于使用163邮箱的用户来说&#xff0c;掌握163邮箱SMTP设置的方法是非常必要的。下面&#xff0c;AokSend就…

javaweb学习(day06-servlet)

一、基本介绍 1 官方文档 地址: https://tomcat.apache.org/tomcat-8.0-doc/servletapi/index.htmlServlet 和 Tomcat 的关系: 一句话, Tomcat 支持 Servlet【假如单独使用Servlet就失去了意义】 2 为什么会出现 Servlet 提出需求: 请用你现有的html css javascript&#…

JavaSE(上)-Day3

JavaSE&#xff08;上&#xff09;-Day3 IDEA&#xff08;目前业内最好用的开发软件&#xff09;初始使用IEDA的基础设置 本篇主要介绍内容如下&#xff1a; IDEA的基本使用IDEA的基础设置 IDEA&#xff08;目前业内最好用的开发软件&#xff09; IDEA是用于Java语言开发的集…

springboot之mybaitsPlus

mybaitsPlus是国内开发的&#xff0c;并不是springboot的项目&#xff0c;只是学习的时候直接就是适配的springboot。 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不…

微服务获取登录用户Id与单体服务下获取用户Id对比(黑马头条Day03)

前置声明 当前前后端分离开发项目中&#xff0c;后端某个请求向具体某个数据库中的多个表插入数据时&#xff0c;经常需要使用到当前登录用户的Id&#xff08;唯一标识&#xff09;。在当前用户线程下以实现变量共享&#xff0c;同时为了避免不同用户线程之间操作变量的影响&am…

设计模式之模版方法实践

模版方法实践案例 实践之前还是先了解一下模版方法的定义 定义 模板方法模式是一种行为设计模式&#xff0c;它定义了一个骨架&#xff0c;并允许子类在不改变结构的情况下重写的特定步骤。模板方法模式通过在父类中定义一个模板方法&#xff0c;其中包含了主要步骤&#xf…

【Python】成功解决TypeError: ‘float‘ object is not iterable

【Python】成功解决TypeError: ‘float’ object is not iterable &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得…

Mysql按照月份分组统计数据,当月无数据则填充0

目录 起因实现结论 起因 最近有个需求需要在sql中实现获取近半年的统计数据&#xff0c;本来以为挺简单的&#xff0c;不过这个项目数据基本没有&#xff0c;在此情况下还要实现获取近半年的数据就没办法简单group by了 实现 #如果每个月都有数据的话是比较简单的 SELECT DA…

【XR806开发板试用】串口驱动JQ8900播放音乐

一、硬件连接 1.JQ8900引脚定义 通过阅读JQ8900的数据手册&#xff0c;可以了解到驱动JQ8900有许多种方式&#xff0c;IO驱动&#xff0c;一线串口驱动&#xff08;VPP&#xff09;&#xff0c;两线串口驱动&#xff08;RX&#xff0c;TX&#xff09;&#xff0c;这里我使用两…

C#多线程(4)——任务并行库TPL

文章目录 1 什么是TPL2 创建与启动任务3 等待任务4 任务中的异常处理5 取消任务 1 什么是TPL T P L \textcolor{red}{TPL} TPL&#xff08;Task Parallel Library&#xff09;任务并行库&#xff0c;是从.NetFramwork4.0后引入的基于异步操作的一组API。TPL的底层是基于多线程实…

C语言第三十六弹---文件操作(中)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 文件操作 1、文件的顺序读写 1.1、顺序读写函数介绍 1.1.1、fgetc 与 fputc 1.1.2、fgets 与 fputs 1.1.3、fscanf 与 fprintf 1.1.4、fread 与 fwrite 1.…

基于状态机的按键消抖实现

摸鱼记录 Day_14 !(^O^)y review 在day_13中以按键状态判断为例学习了状态分析基于状态机的按键消抖原理-CSDN博客 分析得到了下图&#xff1a; 今日任务&#xff1a;完成此过程 !(^O^)y 小梅哥对应视频&#xff1a; 15B 基于状态机的按键消抖Verilog实现_哔哩哔哩…

《乱弹篇(23)读书摘录》

之前逾月&#xff0c;偷得闲遐&#xff0c;在腾迅视频看了两部以宋朝历史为背景的电视剧《后宫》和《清平乐》&#xff0c;觉得好看并觉得有所裨益&#xff0c;但之后就再也难以找到能让人有耐心去看完的一部电视剧了。在一阵儿唏嘘感叹后&#xff0c;便去读书网搜索小说书读&a…