数据结构与算法 | 二叉树查询原理

简介: 数据结构与算法

二叉查询树

19000cb2425e4328abfcc3f06639d71c.png

  • 概述

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

  • 特点

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

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

  • 查找法

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


结点实现原理


插入实现原理

0af944de2c2d4bb1ba14f40080b0c31e.png

int[] arrs = {5,2,1,4,3,9,7,6,8};
  1. 如果树是空树,插入节点就直接放入到根结点
  2. 如果树不是空树,则插入的数字于根结点的数字进行比较

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

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

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

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

总结:

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


遍历实现原理


c9cfc324e65c484ba6053ff58127bede.png

中序遍历:左—根—右

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

总结:

  • 始终贯彻左—根—右的原则、由内层向外层拆分
int[] arrs = {1,2,3,4,5,6,7,8,9};

删除实现原理


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


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


  1. 需要考虑结点的三种情况


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

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


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

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


  • 情况3:待删除的结点有两个子节点

从左子树中找到最大的结点进行删除,并且将最大的结点的值放入到待删除结点

从右子树中找到最小的结点进行删除,并且将最小的结点的值放入(替换)到待删除结点

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


4.删除的节点是根节点的情况


  • 情况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);
    }
}


目录
相关文章
|
7天前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
38 4
机器学习/深度学习 算法 自动驾驶
211 0
|
1月前
|
机器学习/深度学习 算法 搜索推荐
从零开始构建图注意力网络:GAT算法原理与数值实现详解
本文详细解析了图注意力网络(GAT)的算法原理和实现过程。GAT通过引入注意力机制解决了图卷积网络(GCN)中所有邻居节点贡献相等的局限性,让模型能够自动学习不同邻居的重要性权重。
182 0
从零开始构建图注意力网络:GAT算法原理与数值实现详解
|
2月前
|
机器学习/深度学习 算法 文件存储
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
神经架构搜索(NAS)正被广泛应用于大模型及语言/视觉模型设计,如LangVision-LoRA-NAS、Jet-Nemotron等。本文回顾NAS核心技术,解析其自动化设计原理,探讨强化学习、进化算法与梯度方法的应用与差异,揭示NAS在大模型时代的潜力与挑战。
401 6
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
|
2月前
|
传感器 算法 定位技术
KF,EKF,IEKF 算法的基本原理并构建推导出四轮前驱自主移动机器人的运动学模型和观测模型(Matlab代码实现)
KF,EKF,IEKF 算法的基本原理并构建推导出四轮前驱自主移动机器人的运动学模型和观测模型(Matlab代码实现)
|
2月前
|
算法
离散粒子群算法(DPSO)的原理与MATLAB实现
离散粒子群算法(DPSO)的原理与MATLAB实现
112 0
|
3月前
|
机器学习/深度学习 人工智能 编解码
AI视觉新突破:多角度理解3D世界的算法原理全解析
多视角条件扩散算法通过多张图片输入生成高质量3D模型,克服了单图建模背面细节缺失的问题。该技术模拟人类多角度观察方式,结合跨视图注意力机制与一致性损失优化,大幅提升几何精度与纹理保真度,成为AI 3D生成的重要突破。
235 0
|
3月前
|
算法 区块链 数据安全/隐私保护
加密算法:深度解析Ed25519原理
在 Solana 开发过程中,我一直对 Ed25519 加密算法 如何生成公钥、签名以及验证签名的机制感到困惑。为了弄清这一点,我查阅了大量相关资料,终于对其流程有了更清晰的理解。在此记录实现过程,方便日后查阅。
237 1
|
4月前
|
消息中间件 存储 缓存
zk基础—1.一致性原理和算法
本文详细介绍了分布式系统的特点、理论及一致性算法。首先分析了分布式系统的五大特点:分布性、对等性、并发性、缺乏全局时钟和故障随时发生。接着探讨了分布式系统理论,包括CAP理论(一致性、可用性、分区容错性)和BASE理论(基本可用、软状态、最终一致性)。文中还深入讲解了两阶段提交(2PC)与三阶段提交(3PC)协议,以及Paxos算法的推导过程和核心思想,强调了其在ZooKeeper中的应用。最后简述了ZAB算法,指出其通过改编的两阶段提交协议确保节点间数据一致性,并在Leader故障时快速恢复服务。这些内容为理解分布式系统的设计与实现提供了全面的基础。
|
4月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
364 58

热门文章

最新文章