JAVA二叉搜索树(专门用来查找)

news2024/11/22 9:12:10

目录

二叉搜索树又叫二叉排序树,它具有以下特征

二次搜索树的效率

模拟最简二叉搜索树代码

代码片段分析

查找二叉搜索树数据:

如果我们用递归的方法查找数据有什么不一样?

插入数据

删除数据(难点)


二叉搜索树又叫二叉排序树,它具有以下特征

1.左子树 不为空 的时候 左子树上的所有节点 比 根节点 小

2.右子树 不为空 的时候 右子树上的所有节点 比 根节点 大

3.每棵分树仍然是 二叉搜索树

二次搜索树的效率

可以看到如果在使用二叉搜索树查找数据时效率非常高,如查找图中数据60时直接将左树数据排除

一下排除了一半的数据

二叉搜索树的最优形态为完全二叉树:O(log2n)

二叉搜索树的最差形态为单支树:O(n) 

为了避免二叉搜索树变成单分支树我们通过左旋右旋的方式将单分支树变为AVL树入:

为减少AVL树左旋右旋次数就加入颜色变为红黑树

模拟最简二叉搜索树代码

public class MyBinarySearchTree {
    static class Node{
        int val;
        Node left;
        Node right;
        public Node(int val){
            this.val = val;
        }
    }
    // 二叉搜索树 根节点
    Node root = null;
    private int size;


    // 插入数据
    public void put(int val){
        // 查看二叉搜索树是否为空
        // 实例化一个节点
        Node node = new Node(val);
        if(root == null){
            root = node;
            return;
        }
        // 二叉搜索树插入到叶子节点
        Node cur = root;
        Node curFather = null;
        while(cur != null){
            if(val > cur.val){
                curFather = cur;
                cur = cur.right;
            }else if(val < cur.val){
                curFather = cur;
                cur = cur.left;
            }else{
                // 二叉搜索树只用来 查找,
                // 所以遇到相同的值时说明二叉搜索树已经存有,查找时也能查到就无需将重复的数据插入进去了
                System.out.println("二叉搜索树已经存有该数据: "+ val+" 无需继续存储就去");
                return;
            }
        }
        //出了这个循环说明cur已经到了最后null,curFather节点指向二次搜索树叶子节点
        if(val > curFather.val){
            curFather.right = node;
            size++;
        }else{
            curFather.left = node;
            size++;
        }
    }

    // 中序遍历
    private Node inordercur = root;
    public void inorder(Node inordercur){
        if(inordercur == null){
            return;
        }
        inorder(inordercur.left);
        System.out.print(inordercur.val+" ");
        inorder(inordercur.right);
    }


    // 查找二叉搜索树
    public Node find(int val){
        Node cur = root;
//        // 判断根节点是否为空
//        if(cur == null){
//            System.out.println("搜索二叉树节点为空,无法查找");
//            return null;
//        }
        while(cur != null){
            if(val > cur.val){
                cur = cur.right;
            }else if(val < cur.val){
                cur = cur.left;
            }else{
                // 找到相等值
                return cur;
            }
        }
        // 出了此循环说明cur为null,说明查找到数据
        System.out.println("没有查找到: "+ val );
        return null;
    }


    // 查找二叉搜索树方法2
    private Node find2(int val,Node root){
        if(root == null){
            return null;
        }
        if(val == root.val){
            return root;
        }

        Node leftNode = find2(val,root.left);
        if(leftNode != null){
            return leftNode;
        }
        Node rightNode = find2(val,root.right);
        if(rightNode != null){
            return rightNode;
        }
        return null;
    }



    // 删除二叉搜索树
    public void delete(int val){
        if(root == null){
            System.out.println("二叉搜索树为空,无法删除");
            return;
        }

        // 找到要删除的二叉搜索树节点
        Node[] nodeArr = findNode(val);
        // 判断要删除的节点是否为空
        if(nodeArr[0] == null && nodeArr[1] == null){
            System.out.println("要删除节点为空无法删除");
            return;
        }
        Node curFather = nodeArr[0];
        Node deleteCur = nodeArr[1];

        if(deleteCur.left == null){         // 要删除节点 左孩子为空
            // 删除节点为 根节点
            if(deleteCur == root){
//                deleteCur = deleteCur.right;
                root = deleteCur.right;
            // 要删除节点在 删除节点前驱右边
            }else if(curFather.right == deleteCur){
                curFather.right = deleteCur.right;
            // 要删除节点在 删除节点前驱左边
            }else if(curFather.left == deleteCur){
                curFather.left = deleteCur.right;
            }
        }else if(deleteCur.right == null){  // 要删除节点 右孩子为空
            // 删除节点是 根节点
            if(deleteCur == root){
                root = deleteCur.left;
            // 删除节点是 根节点右边
            }else if(curFather.right == deleteCur){
                curFather.right = deleteCur.left;
            }else if(curFather.left == deleteCur){
                curFather.left = deleteCur.left;
            }
        }else{                        // 要删除节点 左右孩子不为空
            // 找要删除节点的 右孩子节点的最左树
            // 也就是比要删除节点大的最小值
            Node cur = deleteCur.right;
            Node lastCur = deleteCur;
            while(cur.left != null){
                lastCur = cur;
                cur = cur.left;
            }
            // 叶子节点覆盖到要删除的 节点
//            deleteCur = cur;
//            root = cur;
            deleteCur.val = cur.val;
            System.out.println("root的数值为: "+root.val);
            if(lastCur.left == cur) {
                lastCur.left = cur.right;
            }else{
                // 要删除节点的 右子树 没有左孩子树
                deleteCur.right = cur.right;
            }
        }
    }

    private Node[] findNode(int val) {
        // cur前驱节点
        Node curFather = null;
        Node cur = root;
        Node[] nodeArr = new Node[2];
        // 找到要删除的二叉搜索树节点
        while(cur != null){
            if(val > cur.val){
                curFather = cur;
                cur = cur.right;
            }else if(val < cur.val){
                curFather = cur;
                cur = cur.left;
            }else{
                // 找到要删除的节点和要删除节点的前驱
                nodeArr[0] = curFather;
                nodeArr[1] = cur;
                break;
            }
        }
        return nodeArr;
    }
}

代码片段分析

初始化:一个二叉搜索树底层仍然是一颗二叉树,每个树有3个域,分别是值域val,左树地址域,右树地址域,一棵二叉搜索树有一个根节点

查找二叉搜索树数据:

方法参数为想要查找的数据值

如果根节点不为空的话

  查找数据val > 根节点的话就往右树找 cur = cur.right

  查找数据val < 根节点的话就往左树找 cur = cur.left

  查找到数据val = 根节点的话就直接返回此节点,说明找到了

  根节点为null或出了循环仍然未找到说明二叉搜索树里没有这个值,返回null

如果我们用递归的方法查找数据有什么不一样?

我们可以发现如果用递归写的话我们就无法用到二叉搜索树的优点,它还是将每个节点查找一次,时间复杂度比较高

插入数据

将数据插入到二叉搜索树里就是将数据插到树的叶子节点

方法参数为想要插入的数据

1.首先根据数据先实例化一个节点

2.判断是否为空,为空的话说明此时插入的节点为二叉搜索树的第一个节点,将新的节点赋值为根节点

3.找到要插入对应的叶子节点位置

   3.1:插入数据 > 根节点,往右树走

   3.2:插入数据 < 根节点,往左树走

   3.3:如果插入数据 = 根节点,退出(因为二叉搜索树是用来查找数据是否存在的,因此只要有一个数据在那就行了)

4.记得放一个父亲节点curFather跟在cur节点后面,当cur循环结束后说明cur节点此时为null,而curFather父亲节点刚好就在最后的叶子位置

5.要插入节点 > curFather叶子节点: curFather.right = 要插入节点

   要插入节点 < curFather叶子节点: curFather.left = 要插入节点

删除数据(难点)

1.判断根节点是否为null,是null直接返回无法删除

2.找到要删除节点和要删除节点的前驱

3.要删除节点的左子树为空时

        3.1:要删除节点刚好是根节点且左树为空: 

              根节点移到要删除节点右边: root = deleteCur.right

        

        3.2:要删除节点不是根节点且左树为空:

              根节点的左树移到要删除节点的右边

        

     4.要删除节点的右子树为空时

            4.1:要删除节点刚好是根节点且右子树为空

                  根节点移到要删除节点左边: root = deleteCur.left 

                 

                4.2要删除节点不是跟节点且右树为空

                        root根节点的左树指向要删除节点的左树: root.left = deleteCur.left

               

5.要删除节点左子树和右子树都不为null(最难点)

1.找要删除的节点和要删除节点的前驱

2.找到要删除节点右子树的最左树 或 要删除节点的左子树的最右树

        要删除节点右子树的最左树就是比删除节点大的最小值

        要删除节点的左子树的最右树就是比删除节点小的最大值

3.替换,

4.将替换后的最左树或最右树删除,删除完毕后可以发现它仍然是一棵 二叉搜索树

5.如果要删除节点的右子树没有最左边 或 要删除节点的左子树没有最右边 怎么办

        5.1:还是先替换,将要删除的节点进行覆盖删除

        5.2:将要删除节点的右孩子树移到cur的右孩子树中,这样替换后的节点就会被删除

                

        

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

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

相关文章

python之pyQt5实例:几何绘图界面

使用PyQt5设计一个界面&#xff0c;其中点击不同的按钮可以在画布上画出点、直线、圆和样条曲线 from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QHBoxLayout,QVBoxLayout,QWidget,QLabel from PyQt5.QtGui import QPainter, QPen, QColor from PyQt5.Q…

nssm将exe应用封装成windows服务

一、简介 NSSM&#xff08;Non-Sucking Service Manager&#xff09;是一个用于在Windows操作系统上管理和运行应用程序作为服务的工具。它提供了一种简单的方法来将任意可执行文件转换为Windows服务&#xff0c;并提供了一些额外的功能和配置选项。 优点&#xff1a; 简单易…

【遍历二叉树算法描述】

文章目录 遍历二叉树算法描述先序遍历二叉树的操作定义中序遍历二叉树的操作定义后序遍历二叉树的操作定义 遍历二叉树算法描述 1.遍历定义&#xff1a;顺着某一条搜索路径寻访二叉树中的结点&#xff0c;使得每一个结点均被访问一次&#xff0c;而且仅访问一次&#xff08;又…

【算法-数组3】螺旋数组(一入循环深似海啊!)

今天&#xff0c;带来数组相关算法的讲解。文中不足错漏之处望请斧正&#xff01; 理论基础点这里 螺旋数组 1. 思路 这道题主要是模拟转圈过程&#xff0c;但是要处理的边界条件比较多&#xff0c;常见的问题就是每条边的处理都有自己的逻辑&#xff0c;那这就很难。如果不…

基于正负序双dq旋转坐标系锁相环 DDSRF-PLL模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; DDSRF-PLL则是通过构建数学解耦网络来消除&#xff12;倍电网频率的交流耦合分量 。由于DDSRF-PLL是在解耦多同步坐标系锁相环的基础上得到的&#xff0c;因此&#xff0c;需要研究解耦多同步坐标系锁相环的组…

高速串行总线—Rapid IO

SRIO简介 Rapid IO 是一种高性能、 低引脚数、 基于数据包交换的互连体系结构&#xff0c;是为满足和未来高性能嵌入式系统需求而设计的一种开放式互连技术标准。RapidIO主要应用于嵌入式系统内部互连&#xff0c;支持芯片到芯片、板到板间的通讯&#xff0c;可作为嵌入式设备的…

第26期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

Vue-router 路由的基本使用

Vue-router是一个Vue的插件库&#xff0c;专门用于实现SPA应用&#xff0c;也就是整个应用是一个完整的页面&#xff0c;点击页面上的导航不会跳转和刷新页面。 一、安装Vue-router npm i vue-router // Vue3安装4版本 npm i vue-router3 // Vue2安装3版本 二、引入…

什么是数据可视化,为什么数据可视化很重要?

数据可视化是数据的图形表示&#xff0c;可以帮助人们更轻松地理解和解释复杂的信息。它涉及创建数据的视觉表示&#xff0c;例如图表、图形、地图和其他视觉元素&#xff0c;以传达数据中的见解、模式和趋势。数据可视化是将原始数据转化为可操作知识的关键工具。 以下是数据…

CSS 背景、文本、字体

CSS背景&#xff1a; CSS背景属性用于定义HTML元素的背景。CSS属性定义背景效果&#xff1a;background-color&#xff1b;background-image&#xff1b;background-repeat&#xff1b;background-attachment&#xff1b;background-position。 background-color属性定义元素…

swift语言用哪种库适合做爬虫?

因为Swift语言并没有在语言层面上支持正则表达式&#xff0c;这对于爬虫来说是一个很大的缺陷。不过&#xff0c;Swift语言可以通过调用其他语言的库来实现爬虫功能&#xff0c;比如可以使用Python的BeautifulSoup库或者JavaScript的Cheerio库来解析HTML页面。但是相比于Python…

【Proteus仿真】【51单片机】汽车尾灯控制设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用按键、LED模块等。 主要功能&#xff1a; 系统运行后&#xff0c;系统运行后&#xff0c;系统开始运行&#xff0c;K1键控制左转向灯&#xff1b;…

第12章 PyTorch图像分割代码框架-2

模型模块 本书的第5-9章重点介绍了各种2D和3D的语义分割和实例分割网络模型&#xff0c;所以在模型模块中&#xff0c;我们需要做的事情就是将要实验的分割网络写在该目录下。有时候我们可能想尝试不同的分割网络结构&#xff0c;所以在该目录下可以存在多个想要实验的网络模型…

JVM虚拟机:垃圾回收器之Parallel Scavenge

本文重点 在前面的课程中,我们学习了新生代的串行化垃圾回收器Serial,本文我们将学习新生代的另外一个垃圾回收器Parallel Scavenge(PS),PS是一个并行化的垃圾回收器,它使用复制算法来清理新生代的垃圾。 运行方式 如上所示,当进行垃圾回收的时候,它会暂停工作线程,而…

第二章: 创建第一个Spring Boot 应用

第二章: 创建第一个Spring Boot 应用 前言 本章重点知识:构建你的第一个Spring Boot应用:以一个简单的例子来引导你进入Spring Boot的开发,包括如何使用Spring Initializr来创建项目,以及如何使用Maven或Gradle构建和运行项目等 IntelliJ IDEA 开发工具中安装 Spring Init…

网络原理---网络初识

文章目录 网络发展史独立模式网络互连局域网LAN广域网WAN 网络通信基础IP地址端口号 认识协议什么是协议&#xff1f;协议分层为什么要分层&#xff1f;两种典型的分层方式&#xff1a;OSI七层TCP/IP五层 网络发展史 从我们出生以来&#xff0c;网络世界就已经纷繁错杂。我们虽…

简单CMake入门

CMake可以生成不同平台下的Makefile&#xff0c;有了CMake不用再写复杂的Makefile 视频教程&#xff1a;CMake 6分钟入门&#xff0c;不用再写复杂的Makefile 先前知识 Makefile简单入门 Cmake特性 CMake是一个用于管理C/C项目的跨平台构建工具。 跨平台&#xff1a;CMake是…

CSS示例001:鼠标放div上,实现旋转、放大、移动等效果

GPT能够很好的应用到我们的代码开发中&#xff0c;能够提高开发速度。你可以利用其代码&#xff0c;做出一定的更改&#xff0c;然后实现效能。 css实战中&#xff0c;经常会看到这样的场景&#xff0c;鼠标放到一个图片或者一个div块状时候&#xff0c;会出现旋转、放大、移动…

webgoat-Insecure Deserialization不安全的序列化

A&#xff08;8&#xff09;不安全的反序列化 反序列化是将已序列化的数据还原回对象的过程。然而&#xff0c;如果反序列化是不安全的&#xff0c;那么恶意攻击者可以在序列化的数据中夹带恶意代码&#xff0c;从而在反序列化时执行这些代码。这种攻击被称为反序列化。 什么…

2023年中国大学生程序设计竞赛女生专场题解, K. RSP

Dashboard - 2023年中国大学生程序设计竞赛女生专场 - Codeforces K. RSP time limit per test1 second memory limit per test512 megabytes input standard input output standard output 小 A 和小 B 在玩一种叫做石头剪刀布的游戏。 这个游戏的规则很复杂&#xff0c…