数据结构之二叉搜索树底层实现洞若观火!

目录

题外话

正题

二叉搜索树

底层实现

二叉搜索树查找操作

查找操作思路

查找代码实现详解

二叉搜索树插入操作

插入操作思路

插入代码详解

二叉搜索树删除操作

删除操作思路

删除代码详解

小结


题外话

我的一切都是党给的,都是人民给的,都是家人们给的!!

十分感谢家人们的大力支持,没有你们就没有我的今天(作者磕头三响!!!)

正题

今天来复习一下二叉搜索树正好完成一下底层实现

二叉搜索树

二叉搜索树概念:如果一颗二叉搜索树的任一结点的左子树不为空,那么左子树一定比该结点值小

                          如果一颗二叉搜索树的任一结点的右子树不为空,那么右子树一定比该结点值大

如下图,就是一颗二叉搜索树

任一一颗二叉搜索树中序遍历一定是有序的

底层实现

我们先将搜索二叉树的结点和结点值,还有左右子树创建出来

static  class TreeNode
{
    //创建结点元素值val
    public int val;


    //创建搜索二叉树的左右子树
    public TreeNode left;
    public TreeNode right;


    //通过TreeNode的构造方法赋值val
    public TreeNode(int val)
    {
        this.val=val;
    }

}
    //创建搜索二叉树的根
    public TreeNode root;

二叉搜索树查找操作

查找操作思路

我们通过搜索二叉树的概念,让key和根结点值比较

如果比根结点大,就和根结点右子树比较

如果比根结点小就和根结点左子树比较

例如上图,key等于9的时候在图中能找到9的结点

而当key等于10的时候,走到结点值为9的位置后左右结点为空,所以找不到等于10的结点返回flase

所以可以很容易写出以下代码

查找代码实现详解

public boolean search(int key)
{
    //将根结点赋值给cur,防止遍历完成找不到搜索二叉树的根节点
    TreeNode cur=root;


    //当cur不为空
    while (cur!=null)
    {


        //如果cur的val值小于key说明key在cur的右边
        if (cur.val<key)
        {
            //cur等于cur的右子树
           cur=cur.right;
        }
        //如果cur的val值比key大,说明key在cur的左边
        else if (cur.val>key)
        {
            //cur等于cur的左子树
            cur=cur.left;
        }
        //如果cur的val值等于key,说明找到了,则返回true
        else {
            return true;
        }
    }
    //如果cur为空还没有找到key说明key在搜索二叉树中不存在返回false
    return false;
}

二叉搜索树插入操作

插入操作思路

1.如果根结点为空,直接让插入元素val插入到根结点位置

2.如果根结点不为空

如果val大于根结点则继续判断root右子树和val大小关系

如果val小于根结点则继续判断root左子树和val大小关系

3.如下图所示,当我们走到结点值为9的位置发现val=10仍然大于9

然后继续往9的右子树走,发现为空

我们将val=10插入9的右子树,但是我们没有记录9这个结点,无法向9这个结点的右子树插入val

所以我们需要记录最近一次到达的上一个结点,以便可以将结点连接在一起

4.如果val是二叉搜索树中的结点值其中之一

比如val=5,这无法插入

因为二叉搜索树中的元素一定是没有重复的

要么比任意结点小,要么比任意结点大

不可能出现两个结点相同的情况

所以当出现插入元素和二叉搜索树中元素相同时,我们选择返回false

插入代码详解

public boolean insert(int val)
{
    //如果根结点为空则将插入结点作为根结点
    if (root==null)
    {
        root=new TreeNode(val);
        return true;
    }
    //如果根结点不为空,将root赋值给cur
    TreeNode cur=root;
    //创建prent记录cur到达的上一个结点
    TreeNode parent=null;
    //当cur不为空
    while (cur!=null) {
        //如果cur的val值比插入值小,则让parent记录当前cur结点,并且cur等于cur的右子树
        if (cur.val < val) {
            parent=cur;
            cur = cur.right;
        }
        //如果cur的val值比插入值大,则让parent记录当前cur结点,并且cur等于cur的左子树
        else if (cur.val > val) {
            parent=cur;
            cur = cur.left;
        }
        //如果cur的val值等于插入值,则无法插入,返回false
        else {
            return false;
        }
    }
    //当cur==null说明cur已经到达了要插入的位置
    TreeNode node=new TreeNode(val);
    //如果插入值比cur到达的上一个结点值大,则插入在上一个结点值的右边
    if (val> parent.val)
    {
        parent.right=node;
    }
    //如果插入值比cur到达的上一个结点值小,则插入在上一个结点值的左边
    else {
        parent.left=node;
    }
    //最后插入完成返回true
    return true;
}

接下来讲解一下搜索二叉树删除操作(有点难)

大家跟紧我的思维!

二叉搜索树删除操作

删除操作思路

首先咱们思考一下,删除搜索二叉树结点元素我们需要考虑什么问题

1.首先要遍历搜索二叉树找到要删除的结点

2.考虑搜索二叉树为空,没有结点的情况,无法删除

3.考虑搜索二叉树删除结点的左子树为空的情况如下图,cur为要删除结点

4.考虑搜索二叉树删除结点的右子树为空的情况,这里就不展示了,和上面情况差不多

5.考虑搜索二叉树删除结点的左子树右子树都不为空的情况(认真理解)

这里统一采用找到删除结点的右子树最深左结点的方法

找到get分两种情况下图是第一种

下图是第二种

以上内容我最开始也想不出来,但是做多了关于二叉树的题之后,也渐渐找到了这类型题的规律

基本上任何二叉树的题都和遍历二叉树有关系,也就是在遍历二叉树的基础上

无非是比较结点大小,或者结点是否存在等等变化,大家可以试着体会一下

删除代码详解

这里用了两个方法

1.第一个方法是找到要删除的结点

2.第二个方法是接收要删除的结点,以及遍历的上一个结点值

//这个方法是用来找到要删除的结点,并调用删除结点方法

public boolean remove(int val)
{
    //当二叉搜索树为空树,则返回false
    if (root==null)
    {
        return false;
    }
    //让root赋值给cur去遍历二叉搜索树
    TreeNode cur=root;
    //设置parent记录上一个遍历的结点
    TreeNode parent=null;
    //当cur不等于空就继续找删除结点
    while (cur!=null) {
        //如果cur的val值比删除值小,则让parent记录当前cur结点,并且cur等于cur的右子树
        if (cur.val < val) {
            parent=cur;
            cur = cur.right;
        }
        //如果cur的val值比删除值大,则让parent记录当前cur结点,并且cur等于cur的左子树
        else if (cur.val > val) {
            parent=cur;
            cur = cur.left;
        }
        //如果cur的val值等于删除值,则说明cur所在就是要删除的结点
        // 则调用remoNode方法,传入删除结点cur和parent
        else {
            removeNode(cur,parent);
            return true;
        }

    }
    //当cur等于空就说明二叉搜索树中不存在删除结点,返回false
    return false;
}

//这个方法负责接收要删除的结点和上一个到达的结点,并删除结点值

private void removeNode(TreeNode cur,TreeNode parent)
{
    //一.如果要删除结点的左子树为空(三种情况)
    if (cur.left==null)
    {
        //1.如果根结点等于要删除结点cur
        if (root==cur)
        {
            //让root指向root的右子树
            root=root.right;
        }
        //2.如果cur为parent的左子树
        else if (cur==parent.left)
        {
            //则让parent的左子树指向cur.right的右子树
            parent.left=cur.right;
        }
        //3.如果cur为parent的右子树
        else {
            //则让parent的右子树指向cur的右子树
            parent.right=cur.right;
        }
    }
    //二.当要删除结点cur的右子树为空时(三种情况)
    else if (cur.right==null)
    {
        //1.如果cur等于根结点
        if (cur==root)
        {
            //让根结点指向cur的左子树
            root=cur.left;
        }
        //2.如果cur等于parent的左子树
        else if(cur==parent.left)
        {
            //则让parent的左子树指向cur的左子树
            parent.left=cur.left;
        }
        //3.如果cur等于parent的右子树
        else {
           //则让parent的右子树指向cur的左子树
            parent.right=cur.left;
        }
    }
    //三.如果左子树右子树都不为空
    else {
        //设置getParent指向cur,设置get指向cur的右子树
        TreeNode getParent=cur;
        TreeNode get=cur.right;
        //如果get的左子树不为空,就去找到cur.right的最深左结点,并让getPatent记录上一个到达的结点
        while (get.left!=null)
        {
            getParent=get;
            get=get.left;
        }
        //当get左子树为空,说明找到了最深左结点,让get的val覆盖cur的val
        cur.val=get.val;
        //如果cur.right有左子树,getParent的左子树一定会等于get结点
        if (getParent.left==get)
        {
            //get不可能有左子树了,所以直接让getParent左子树指向get的右子树,图中第一种情况
            getParent.left=get.right;
        }
        //如果cur.right没有左子树,直接让getParent的右子树指向get的右子树即可,图中第二种情况
        else {
            getParent.right=get.right;
        }
    }
}

小结

今天是真的超级努力

早上从10点多起床,洗漱然后吃饭,从十一点半学到了现在,七个多小时,晚上还有课三个小时

最热爱的一集!!

麻烦喜欢的家人们给个三连好嘛!!!(点赞关注收藏!!!)

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

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

相关文章

Docker的数据管理、网络通信和dockerfile

目录 一、Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 2. 数据卷容器 2.1 创建数据卷容器 2.2 使用--volume-from来挂载test1 二、端口映射 三、容器互联 1. 创建容器互联 ​编辑2. 进入test2测试&#xff08;ping 容器名/别名&#xff09; 四、Dock…

c++11详解

目录 1.列表初始化 2.声明 3.右值引用和移动语句 4. c11新的类功能 5. 可变参数模板 6.lambda表达式 7.包装器 8. 后言 1. 列表初始化 1.1 {}的初始化 (1) c98标准规定可以使用{}对数组以及结构体进行统一的列表初始化. struct Point {int _x;int _y; };int main() {in…

【Unity】shader中参数传递

1、前言 unity shader这个对于我来说是真的有点难&#xff0c;今天这篇文章主要还是总结下最近学习到的一些东西&#xff0c;避免过段时间忘记了&#xff0c;可能有不对&#xff0c;欢迎留言纠正。 2、参数传递的两种方式 2.1 语义传递 语义传递这个相对来说是简单的 shad…

数组模拟几种基本的数据结构

文章目录 数组模拟单链表数组模拟双链表数组实现栈数组模拟队列总结 数组模拟单链表 首先类比结构体存储单链表&#xff0c;我们需要一个存放下一个节点下标的数组&#xff0c;还需要一个存储当前节点的值的数组&#xff0c;其次就是一个int类型的索引&#xff0c;这个索引指向…

Python 实现视频去抖动技术

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 视频去抖动是视频处理中的一项重要技术&#xff0c;它可以有效地减少视频中由于相机震动或手…

嵌入式开发学习--进程、线程

什么是进程 进程和程序的区别 概念 程序&#xff1a;编译好的可执行文件&#xff0c;存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09;&#xff0c;程序是静态的&#xff0c;没有任何执行的概念。 进程&#xff1a;一个独立的可调度的任务&#xff0c;执行一…

恶补《操作系统》3_1——王道学习笔记

3内存管理 3.1_1 内存的基础知识 1、什么是内存&#xff0c;作用 &#xff08;1&#xff09;内存&#xff1a;内存用来存放数据。程序执行前需要先放到内存中才能被CPU处理――缓和CPU与硬盘之间的速度矛盾。 &#xff08;2&#xff09;内存存储单元&#xff1a;每个地址对应…

AIGC技术的发展现状和未来趋势

AIGC&#xff08;人工智能生成内容&#xff09;技术是指利用人工智能算法自动生成文本、图像、音频、视频等各类内容的技术。随着深度学习等技术的快速发展&#xff0c;AIGC技术在最近几年取得了显著进步&#xff0c;并在多个领域展现出巨大的潜力。 ​ 编辑 发展现状&#x…

ARM功耗管理背景及挑战

安全之安全(security)博客目录导读

服务器网站漏洞怎么修复

服务器网站漏洞的修复是一个关键且复杂的过程&#xff0c;涉及到多个层面的安全加固。以下是一个关于如何修复服务器网站漏洞的详细指南。安全狗专业做服务器安全&#xff0c;有任何服务器安全问题都可以找安全狗哦. ​一、识别和分析漏洞 首先&#xff0c;要确定服务器网站存在…

Linux下的基本指令(1)

嗨喽大家好呀&#xff01;今天阿鑫给大家带来Linux下的基本指令&#xff08;1&#xff09;&#xff0c;下面让我们一起进入Linux的学习吧&#xff01; Linux下的基本指令 ls 指令pwd命令cd 指令touch指令mkdir指令(重要)rmdir指令 && rm 指令(重要)man指令(重要)cp指…

基于 NXP iMX8MM 测试 Secure Boot 功能

By Toradex秦海 1). 简介 嵌入式设备对于网络安全的要求越来越高&#xff0c;而 Secure boot就是其中重要的一部分。 NXP i.MX8MM/i.MX8MP 处理器基于 HABv4 特性来提供 Secure boot 启动过程中的 Chain of Trust&#xff1b; HABv4 是基于公共密钥加密 (Public Key Cryptogr…

C语言进阶:指针的进阶(上)

首先 在学习新知识之前 我们先来回顾下之前的学习的内容 1 指针是个变量 用来存放地址 地址唯一标识的一块内存空间 2 指针的大小是固定的4/8字节&#xff08;32位平台/64位平台&#xff09; 3 指针有类型的 指针的类型决定了两点 一个是指针操作的权限以及整数的步长 4 指针的…

神经网络项目:全连接网络和卷积网络实现水果三分类项目

水果三分类项目 Git源码&#xff1a;传送门 水果种类&#xff1a;草莓、树莓、桑葚 0&#xff1a;草莓 strawberry1&#xff1a;树莓 raspberry2&#xff1a;桑葚 mulberry 项目设计 获取数据 spider.py数据清洗 cleaner.py自定义数据集 dataset.py网络构建 net.py训练模型 t…

git工具简单使用

文章目录 git上传克隆README.gitignore常用指令冲突 git 进行版本控制的版本控制器。安装git yum install -y git 配置git git config --global user.email "youexample.com" 告诉git你的邮箱是什么&#xff1f;最好输入你的gitee的注册邮箱git config --global …

人工智能(pytorch)搭建模型28-基于Transformer的端到端目标检测DETR模型的实际应用,DETR的原理与结构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型28-基于Transformer的端到端目标检测DETR模型的实际应用&#xff0c;DETR的原理与结构。DETR&#xff08;Detected Transformers&#xff09;是一种基于Transformer的端到端目标检测模型&…

把 KubeBlocks 跑在 Kata 上,真的可行吗?

背景 容器的安全性一直是广受关注的话题。这个领域也产生了很多不错的开源项目。Kata就是其中之一。 Kata Containers&#xff08;简称 Kata&#xff09;是一种开源项目&#xff0c;它提供了一种安全而高性能的容器运行时环境。Kata Containers 利用虚拟化技术&#xff08;通常…

【算法刷题 | 贪心算法03】4.25(最大子数组和、买卖股票的最佳时机|| )

文章目录 4.最大子数组和4.1题目4.2解法一&#xff1a;暴力4.2.1暴力思路4.2.2代码实现 4.3解法二&#xff1a;贪心4.3.1贪心思路4.3.2代码实现 5.买卖股票的最佳时机||5.1题目5.2解法&#xff1a;贪心5.2.1贪心思路5.2.2代码实现 4.最大子数组和 4.1题目 给你一个整数数组 n…

Mac下XDebug安装

文章目录 1、下载对应的版本2、编译XDebug3、配置XDebug4、配置PhpStormDebug一下 前置工作 Mac下安装HomebrewMac下brew安装php7.4 1、下载对应的版本 首先按照支持的版本和兼容性来下载对应的版本&#xff0c;此表列出了仍支持哪些 Xdebug 版本&#xff0c;以及哪些版本可用…

GPT的全面历史和演变:从GPT-1到GPT-4

人工智能新篇章&#xff1a;GPT-4与人类互动的未来&#xff01; 本文探讨了生成式预训练 Transformer (GPT) 的显着演变&#xff0c;提供了从开创性的 GPT-1 到复杂的 GPT-4 的旅程。 每次迭代都标志着重大的技术飞跃&#xff0c;深刻影响人工智能领域以及我们与技术的互动。 我…