Java二叉树查询原理深入分析讲解

目录
  • 二叉查询树
  • 结点实现原理
    • 插入实现原理
    • 遍历实现原理
    • 删除实现原理
    • 结点插入与遍历案例

二叉查询树

概述

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分

特点

树同时具有数组查询的效率、链表增删、改的性能

右子树的结点比左子树的节点大

查找法

搜索的数字如果比节点大则往右搜索,搜索的数字如果比节点小则往左搜索

结点实现原理

插入实现原理

int[] arrs = {5,2,1,4,3,9,7,6,8};

如果树是空树,插入节点就直接放入到根结点

如果树不是空树,则插入的数字于根结点的数字进行比较

如果插入的值小于于结点的数字,则往左子树插入

  • 如果左子结点没有元素就插入到左子结点中
  • 如果左子结点有元素,就可以设计一个引用(游标)指向左子节点,并且再次和待插入的执行左子结点进行比较,直到找到插入的位置

如果插入的值大于结点的数字,则往右子树插入

  • 判断右子结点是否存在值,如果不存在则直接插入
  • 判断右子结点是否存在值,如果存在则通过一个引用指向右子结点继续和待插入的值进行比较,直到找到插入的位置

总结:

  • 小往左,大往右
  • 左子数永远小于右子树

遍历实现原理

中序遍历:左—根—右

通过中序遍历就可以将二叉树查找树的进行顺序输出

总结:

始终贯彻左—根—右的原则、由内层向外层拆分

int[] arrs = {1,2,3,4,5,6,7,8,9};

删除实现原理

提供一个待删除的结点的值,根据值从二叉查找树找到需要删除的结点

找到待删除结点的父类结点,并且要根据待删除结点在父类结点的左右子树的位置,设置为null进行删除

需要考虑结点的三种情况

情况1:待删除的结点没有子结点

直接让父类结点的对应目标结点引用设置为null即可

情况2:待删除的结点有一个子节点

将待删除的父类结点对应子节点的引用指向待删除结点的子节点

情况3:待删除的结点有两个子节点 从左子树中找到最大的结点进行删除,并且将最大的结点的值放入到待删除结点从右子树中找到最小的结点进行删除,并且将最小的结点的值放入(替换)到待删除结点

(上述两种删除方法:需要将待删除结点指向新创建(替换后的)的结点,并且将新的结点(替换后的)的左右结点指向待删除的左右子树的结点)

删除的结点是根节点的情况

情况1:根节点没有子节点,直接将根结点指向null

情况2:根结点有一个子节点,则根结点直接指向子节点

情况3:根结点有两个子节点

可以从左子树中找到最大值删除结点,然后将最大值覆盖(替换)根节点

可以从右子树中找到最小值删除结点,然后将最小值覆盖(替换)根节点

结点插入与遍历案例

BinarySearchTree类

package Algorithm;
public class BinarySearchTree {
    Node root;  //定义根节点
    //结点插入方法
    public void insert(int value) {
        if (root == null) {        //1.如果树是空树,插入节点就直接放入到根结点
            root = new Node(value);
        } else {     //如果树不是空树,则插入的数字于根结点的数字进行比较
            //2.如果插入的值小于于结点的数字,则往左子树插入
            Node node = root;     //声明一个游标结点,开始指向根节点
            while (true) {       //并且再次和待插入的执行左子结点进行比较,直到找到插入的位置
                if (value < node.value) {      //如果插入的值小于于结点的数字,则往左子树插入
                    //2.1如果左子结点没有元素就插入到左子结点中
                    if (node.left == null) {
                        node.left = new Node(value);
                        break;      //如果找到插入的位置,则跳出while循环
                    } else {         //如果左子结点有元素,就可以设计一个引用(游标)指向左子节点,并且再次和待插入的执行左子结点进行比较,直到找到插入的位置
                        //游标指向左子节点
                        node = node.left;
                    }
                } else {      //如果插入的值大于结点的数字,则往右子树插入
                    //判断右子结点是否存在值,如果不存在则直接插入
                    if (node.right == null) {
                        node.right = new Node(value);
                        break;
                    } else {     //判断右子结点是否存在值,如果存在则通过一个引用指向右子结点继续和待插入的值进行比较,直到找到插入的位置
                        //游标指向右子节点
                        node = node.right;
                    }
                }
            }
        }
    }
    //定义左右结点常量
    public static final int LEFT = 0; //左子节点
    public static final int RIGHT = 1; //右子节点
    //结点查找方法
    public void deleteNode(int value) {
        //定义游标从根节点开始查询
        Node node = root;
        //定义目标结点
        Node target = null;
        //定义目标结点的父类结点
        Node parent = null;
        //目标结点的类型为,左子节点或者右子节点
        int nodeType = 0; //0代表左子节点 1代表右子节点

        while (node != null) { //游标不为空,如果为空则没有子节点,无法删除
            if (node.value == value) { //如果目标结点的值和需要删除结点的值相同
                //找到结点
                target = node;
                break;
            } else if (value < node.value) {    //如果值不同,则判断目标结点值是否小于node结点
                //保存父类结点
                parent = node;
                //游标指向左子节点
                node = node.left;
                nodeType = LEFT;
            } else { //如果值不同,且目标结点值大于node结点
                //保存父类结点
                parent = node;
                //游标指向右子节点
                node = node.right;
                nodeType = RIGHT;
            }
        }
        //如果没找到需要删除的目标结点
        if (target==null){
            System.out.println("没有找到要删除的结点");
            return;
        }
        //删除结点的三种情况
        if (target.left == null && target.right == null) {   //情况1:待删除的结点没有子结点

            if (parent==null){      //删除的结点没有子结点
                //将root设置为null即可
                root=null;
                return;
            }
            //判断目标的结点是左子节点还是右子节点
            if (nodeType == LEFT) {
                //将父类的左子节点设置为null
                parent.left = null;
            } else {
                //将父类的右子节点设置为null
                parent.right = null;
            }
        } else if (target.left != null && target.right != null) {   //情况2:待删除的结点有2个子节点
            //两个子节点,从target右子树查找最小的值
            Node min=target.right;
            //遍历左子树
            while (min.left!=null){
                min = min.left;
            }
            //将最小的结点进行删除
            deleteNode(min.value);
            //将待删除的结点替换成最小的结点的值
            target.value= min.value;
        }else { //情况3:待删除的结点有1个子节点
            //删除结点是根节点
            if (parent==null){
                if (target.left!=null){ //判断是左子节点还是右子节点有值
                    root=target.left;   //根节点=目标左子结点
                }else {
                    root=target.right;  //根节点=目标右子结点
                }
            }
            //只有一个子节点
            if (nodeType==LEFT){    //如果是左子节点
                if (target.left!=null){
                    //将父类的左子节点,指向待删除结点的左子节点
                    parent.left=target.left;
                }else { //如果是右子节点
                    //将父类的左子节点,指向待删除结点的右子节点
                    parent.left=target.right;
                }
            }else {
                if (target.right!=null){
                    //将父类的右子节点,指向待删除结点的左子节点
                    parent.right=target.left;
                }else { //如果是右子节点
                    //将父类的右子节点,指向待删除结点的右子节点
                    parent.right=target.right;
                }
            }
        }
    }
    //实现中序遍历
    public void midTraversal(Node node) {
        if (node == null) {  //进行判断结点不能为空,如果为空则退出
            return;
        } else {     //如果结点不为null,则执行下列遍历语句
            //首先,遍历左节点
            midTraversal(node.left);
            //打印根节点
            System.out.print(node.value + ",");
            //最后遍历右子结点
            midTraversal(node.right);
        }
    }
    //创建一个结点类
    public static class Node {
        int value;  //存储值
        Node left;  //左子树
        Node right; //右子树
        // 带参构造方法,传入value赋值
        public Node(int value) {
            this.value = value;
        }
    }
}

TestBST测试类

package Algorithm;
public class TestBST {
    public static void main(String[] args) {
        int[] arrs = {5, 2, 1, 4, 3, 9, 7, 6, 8};
        //创建二叉查询树
        BinarySearchTree tree = new BinarySearchTree();
        //将数组中的元素构造成二叉查询树
        for (int i = 0; i < arrs.length; i++) {
            tree.insert(arrs[i]);
        }
        //删除结点
        tree.deleteNode(20);
        //中序遍历根结点
        tree.midTraversal(tree.root);
    }
}

到此这篇关于Java二叉树查询原理深入分析讲解的文章就介绍到这了,更多相关Java二叉树查询内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 数据结构进阶二叉树题集上

    目录 1.二叉树的遍历 (1)前.中.后序遍历 (2)层序遍历 2.获取树中子结点的个数 3.获取二叉树的高度 4.判断是不是完全二叉树 5.判断两个树是否相同 6.另一棵树的子树 7.判断平衡二叉树 二叉树操作的代码大多数使用递归来实现,代码会比较简洁,如果使用非递归,代码会比较的繁荣,而且不易理解.(上)中的题偏向于基础,后面(下)中的题机会比较难. 1.二叉树的遍历 (1)前.中.后序遍历 这里写到的遍历是递归遍历,代码比较简单,后续会写非递归的代码.以前序遍历为例: 如果根节点root为

  • Java实现二叉树的基本操作详解

    目录 1. 二叉树结点的构成 2. 二叉树的遍历 2.1 前序遍历 2.2 中序遍历 2.3 后序遍历 3. 获取整棵二叉树的节点个数 4. 获取二叉树叶子节点的个数 5. 获取第K层节点的个数 6. 获取二叉树的高度(深度) 7. 在二叉树中寻找目标值 8. 判断二叉树是不是完全二叉树 9. 层序遍历 1. 二叉树结点的构成 这里采用的是孩子表示法, 所以节点类(使用的是静态内部类)中除了数值域外要有两个引用来表示节点的左子树和右子树. static class TreeNode { publ

  • Java 数据结构进阶二叉树题集下

    目录 1.对称二叉树 2.创建并遍历二叉树 3.二叉树中两节点最近公共祖先 4.二叉搜索树与双向链表 5.根据前序和中序遍历结果创建二叉树 6.二叉树创建字符串 7.非递归实现二叉树前序遍历 8.非递归实现二叉树后序遍历 1.对称二叉树 [OJ链接] 分为以下几种情况: 二叉树为空,是对称二叉树 二叉树不为空,其左子树或者右子树为空,不是对称二叉树 二叉树不为空,左右子树都为空,是对称二叉树 二叉树不为空,左右子树不为空,左右子节点值不同,不是对称二叉树 二叉树不为空,左右子树不为空,左右子节点

  • Java数据结构之树和二叉树的相关资料

    目录 什么是树? 简单认识树 树的概念 树的表示形式 二叉树 二叉树的概念 特殊的二叉树 二叉树的性质 二叉树性质相关习题 实现二叉树的基本操作 了解二叉树的存储结构 简单构造一棵二叉树 二叉树的前序遍历 二叉树的中序 获取二叉树节点的个数 获取二叉树叶子节点个数 获取第k层的节点个数 获取二叉树的高度 检测值为value的元素是否存在 层序遍历 判断一棵二叉树是否为完全二叉树 什么是树? 简单认识树 在生活中,有杨树,石榴树,枣树,而在计算机中的树呢,是一种非线性结构,是由 n(n>=0) 个

  • Java HashMap源码深入分析讲解

    1.HashMap是数组+链表(红黑树)的数据结构. 数组用来存放HashMap的Key,链表.红黑树用来存放HashMap的value. 2.HashMap大小的确定: 1) HashMap的初始大小是16,在下面的源码分析中会看到. 2)如果创建时给定大小,HashMap会通过计算得到1.2.4.8.16.32.64....这样的二进制位作为HashMap数组的大小. //如何做到的呢?通过右移和或运算,最终n = xxx11111.n+1 = xx100000,2的n次方,即为数组大小 s

  • React Hooks核心原理深入分析讲解

    目录 Hooks 闭包 开始动手实现 将useState应用到组件中 过期闭包 模块模式 实现useEffect 支持多个Hooks Custom Hooks 重新理解Hooks规则 React Hooks已经推出一段时间,大家应该比较熟悉,或者多多少少在项目中用过.写这篇文章简单分析一下Hooks的原理,并带大家实现一个简易版的Hooks. 这篇写的比较细,相关的知识点都会解释,给大家刷新一下记忆. Hooks Hooks是React 16.8推出的新功能.以这种更简单的方式进行逻辑复用.之前

  • Java 线程池原理深入分析

    Java 线程池原理 Executor框架的两级调度模型 在HotSpot VM的模型中,Java线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也被销毁回收,而操作系统会调度所有线程并将它们分配给可用的CPU. 在上层,JAVA程序会将应用分解为多个任务,然后使用应用级的调度器(Executor)将这些任务映射成固定数量的线程:在底层,操作系统内核将这些线程映射到硬件处理器上. Executor框架类图 在前面介绍的JA

  • Java开发深入分析讲解二叉树的递归和非递归遍历方法

    目录 前言 1.递归遍历 2.非迭代遍历 3.二叉树的统一迭代法 前言 二叉树的遍历方法分为前序遍历,中序遍历,后续遍历,层序遍历. 1.递归遍历 对于递归,就不得不说递归三要素:以前序遍历为例 递归入参参数和返回值 因为要打印出前序遍历节点的数值,所以参数里需要传入List在放节点的数值,除了这一点就不需要在处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下: public void preorder(TreeNode root, List<Integer> resu

  • Java分页查询--分页显示(实例讲解)

    当数据库中数据条数过多时,一个页面就不能显示,这是要设置分页查询,首先要使用的是数据库sql语句的limit条件实现分组查询 sql语句大概形式为: select * from table limit 开始索引,显示条数 用该语句就会实现分块查询,并且每页显示固定条数. 首先要实现后台分页,我们需要知道它有多少页,每页有多少行,这就需要知道一共多少行,调用sql语句时还需要知道每一页的开始索引,开始索引是根据当前页数算出来的,所以还需要知道当前页数,查询后会返回一个列表存储当前页数据.将这些属性

  • SQL查询的底层运行原理深入分析

    前言 SQL 语言无处不在.SQL 已经不仅仅是技术人员的专属技能了,似乎人人都会写SQL,就如同人人都是产品经理一样.如果你是做后台开发的,那么CRUD就是家常便饭.如果你是做数仓开发的,那么写SQL可能占据了你的大部分工作时间.我们在理解 SELECT 语法的时候,还需要了解 SELECT 执行时的底层原理.只有这样,才能让我们对 SQL 有更深刻的认识.本文分享将逐步分解SQL的执行过程,希望对你有所帮助. 数据准备 本文旨在说明SQL查询的执行过程,不会涉及太复杂的SQL操作,主要涉及两

  • java中Base64编码原理实例讲解

    什么是 Base64 编码 Base64 编码是最常见的编码方式,基于 64 个可打印字符来表示任意二进制数据的方法,是从二进制转换到可见字符的过程. 使用场景 数据加密或签名通过 Base64 转换为字符串存储或传输. 不能传输文件的网络环境可以转换 Base64 进行网络传输. 在文本资源(如 HTML 和 CSS文件)中嵌入图片文件或其他二进制资源. 在 URL.网页中传输少量二进制数据等等. Base64 编码原理 原理是把每 3 个字节(每个字节为 8 位, 3 个字节为 24 位)重

  • Java之哈夫曼压缩原理案例讲解

    1. 哈夫曼压缩原理 首先要明确一点,计算机里面所有的文件都是以二进制的方式存储的. 在计算机的存储单元中,一个ASCII码值占一个字节,1个字节等于8位(1Byte = 8bit) 可以参考这个网站: ASCII码在线转换计算器 以"JavaJavaJavaJavaJavaJava"这个字符串为例,它在计算机内部是这样存储的(每一个字符的ASCII码转换为二进制存储起来): public static void main(String[] args) { String beforeS

  • Java深入分析讲解反射机制

    目录 反射的概述 获取Class对象的三种方式 通过反射机制获取类的属性 通过反射机制访问Java对象的属性 反射机制与属性配置文件的配合使用 资源绑定器 配合使用样例 通过反射机制获取类中方法 通过反射机制调用Java对象的方法 通过反射机制获取类中的构造方法 通过反射机制创建对象(调用构造方法) 通过反射机制获取一个类的父类和父接口 反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的

  • Java Session会话追踪原理深入分析

    目录 一.会话技术 二.Session 1.原理 2.特点 3.获得一个会话对象 4.Session常见方法 三.Cookie和Session的区别 一.会话技术 客户端和服务器通信的过程中,自然而然的会产生一些数据交互.比如,A用户登录了邮箱,那么web服务器该怎么知道C一段时间后的登录状态呢?虽然HttpServletRequest对象和ServletContext对象都可以保存数据,但是不适用于这种情况. 客户端的每次请求,服务器都会产生一个HttpServletRequest对象,该对象

随机推荐