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

目录

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

二次搜索树的效率

模拟最简二叉搜索树代码

代码片段分析

查找二叉搜索树数据:

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

插入数据

删除数据(难点)


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

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.mfbz.cn/a/118658.html

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

相关文章

C++ day3作业

1> 思维导图 2> 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void s…

【EMD】1.初识经验模态分解EMD

/*** poject 经验模态分解及其衍生算法的研究及其在语音信号处理中的应用* author jUicE_g2R(qq:3406291309)* * language MATLAB/Python/C/C* EDA Base on matlabR2022b* editor Obsidian&#xff08;黑曜石笔记软件&#xff09;* * copyright 2023* …

LED点阵显示原理(取字模软件+Keil+Proteus)

前言 写这个的时候我还是有点生气的&#xff0c;因为发现完全按照书上面的步骤来&#xff0c;结果发现不理想&#xff0c;后面还是自己调试才解决了。-_-说多了都是泪&#xff0c;直接进入正文。 软件的操作还是参考我之前的博客。 LED数码管的静态显示与动态显示&#xff0…

nssm将exe应用封装成windows服务

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

ifream标签中的子页面,操作父页面的元素

问题描述&#xff1a;子页面内容发生变化时&#xff0c;导航栏不会跟切换 解决办法&#xff1a; window.parent.document.getElementById demo html1 <html> <head><meta charset"UTF-8"><!-- import CSS --><link rel"stylesh…

一站式解决方案:体验亚马逊轻量服务器/VPS的顶级服务与灵活性

文章目录 一、什么是轻量级服务器/VPS 二、服务器创建步骤 三、服务器连接客户端(私钥登录) 四、使用服务器搭建博客网站 五、个人浅解及总结 一、什么是轻量级服务器/VPS 亚马逊推出的轻量级服务器/VPS&#xff1a;是一种基于云计算技术的虚拟服务器解决方案。它允许用户…

Spring Boot 整合SpringSecurity和JWT和Redis实现统一鉴权认证

&#x1f4d1;前言 本文主要讲了Spring Security文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日一句&#xff1a;努力…

XShelll-修改快捷键-xftp-修改编辑器

文章目录 1.XShelll-修改快捷键2.Xftp-修改文本编辑器3.总结 1.XShelll-修改快捷键 工具>选项 鼠标键盘&#xff0c;右键编辑&#xff0c;新建快捷键。 复制粘贴改成shiftc,shiftv。更习惯一些。 2.Xftp-修改文本编辑器 xftp修改服务器文件默认的编辑器&#xff0c;是记…

重新思考边缘负载均衡

本文介绍了Netflix在基于轮询的负载均衡的基础上&#xff0c;集成了包括服务器使用率在内的多因素指标&#xff0c;并对冷启动服务器进行了特殊处理&#xff0c;从而优化了负载均衡逻辑&#xff0c;提升了整体业务性能。原文: Rethinking Netflix’s Edge Load Balancing[1] 我…

2023李宏毅机器学习HW05样例代码中文注释版

这里只是 2023 李宏毅机器学习 HW05 样例代码的中文注释版的分享&#xff0c;下面的内容绝大部分是样例代码&#xff0c;补充了小部分函数的功能解释&#xff0c;没有做函数功能上的修改&#xff0c;是 Simple baseline 版本。 notebook 代码下载: [EN] [ZH] 文章目录 作业描述…

lazarus:数据集快速导出为excel、csv、sql及其他多种格式

lazarus被成为快速开发工具&#xff0c;为什么说“快速”&#xff0c;重要的一点是&#xff0c;很多工具是现成的&#xff0c;可以拿来直接就用。比如数据导出&#xff0c;如果需要把数据集导出为excel格式文件&#xff0c;写代码可能需要很多时间。lazarus就不用了&#xff0c…

微信小程序文件上传wx.uploadFile

网页版查看了一下负载要求是这样 wx.uploadFile({url: ${wx.getStorageSync(apiUrl)}//sysFileInfo/upload?token${wx.getStorageSync(token)}, // 仅为示例&#xff0c;非真实的接口地址filePath: files[0].url,name: file,formData: {secretFlag: Y },success: (res) > {…

Jakarta-JVM篇

文章目录 一.前言1. 1 JVM-堆常用调参1.2 JVM-方法区常用参数1.3 JVM-codeCache 二.JVM内存结构三. 对象创建四. JVM垃圾回收算法4.1 可达性分析算法4.1.1 对象引用4.1.2 回收方法区. 4.2 分代回收4.3 标记清除4.4 标记复制4.5 标记整理 五.垃圾回收器5.1 根节点枚举5.2 安全点…

XSAN数据恢复-存储空间架构迁移时误格式化存储系统的XSAN数据恢复案例

XSAN数据恢复环境&#xff1a; 昆腾存储&#xff0c;MAC OS操作系统&#xff0c;存放视频类数据&#xff08;MXF、MOV等格式文件&#xff09;。 XSAN故障&检测&#xff1a; 将存储空间从XSAN架构迁移到STORNEXT架构后&#xff0c;存储空间中数据全部丢失。 故障存储中一共…

JsonPath 数据快速查找和提取工具

常用语法 表达式说明$表示根元素$.key选择根元素下的指定键名的值$.*选择根元素下的所有属性值$.array[*]选择根元素中的数组的所有元素$.key[subkey]选择根元素中的键名为key&#xff0c;子键名为subkey的值$.key[*].subkey选择根元素中的键名为key的所有元素的子键名为subke…

软件测试/测试开发丨如何利用ChatGPT自动生成测试用例思维导图

点此获取更多相关资料 简介 思维导图是一种用图形方式表示思维和概念之间关系的工具&#xff1a; 有些公司会使用思维导图编写测试用例&#xff0c;这样做的优点是&#xff1a; 1.可视化和结构化。 2.易于理解&#xff0c;提高效率。 而 ChatGPT 是无法直接生成 xmind 格式…

基于单片机的超声波测距仪

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、本课题研究的主要内容二、超声波测距仪的整体方案2.2 超声波测距仪设计原理 三、超声波测距仪系统硬件电路的设计3.1 超声波测距仪的基本结构 四、 超声波测距仪系统的软件设计4.1 主程序软件设计仿真 五、结…

钉钉企业微应用开发C#+VUE

钉钉相信很多人都用过或听过&#xff0c;企业OA审批&#xff0c;考勤&#xff0c;沟通方方面面都支持。但是有的需求自定义表单的无法满足&#xff0c;例如带有业务特性的数据来源&#xff0c;可能是内部其他系统&#xff0c;以及数据筛选分析没有那么方便&#xff0c;钉钉官方…

网络渗透课2

题目 1、Kali虚拟机采用桥接模式&#xff1b;物理机连接Guet-WiFi&#xff0c;Kali中查看网络配置并截图&#xff0c;能获得IP地址吗&#xff1f; 2、Kali虚拟机采用桥接模式&#xff1b;物理机连接手机热点&#xff0c;Kali中查看网络配置并截图&#xff0c;能获得IP地址吗&a…

公会发展计划(GAP):经过实战考验的 Web3 任务模式

2020 年 12 月&#xff0c;Yield Guild Games 踏上了一段征程&#xff0c;以表彰兢兢业业的 Web3 游戏玩家所付出的时间和努力&#xff0c;同时为他们提供利用自己的技能促进个人成长的机会。这一旅程的第一步是于 2022 年 7 月推出的公会发展计划&#xff08;GAP&#xff09;。…