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

简介: 数据结构与算法

二叉查询树

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);
    }
}


目录
相关文章
|
17天前
|
机器学习/深度学习 算法 PyTorch
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
软演员-评论家算法(Soft Actor-Critic, SAC)是深度强化学习领域的重要进展,基于最大熵框架优化策略,在探索与利用之间实现动态平衡。SAC通过双Q网络设计和自适应温度参数,提升了训练稳定性和样本效率。本文详细解析了SAC的数学原理、网络架构及PyTorch实现,涵盖演员网络的动作采样与对数概率计算、评论家网络的Q值估计及其损失函数,并介绍了完整的SAC智能体实现流程。SAC在连续动作空间中表现出色,具有高样本效率和稳定的训练过程,适合实际应用场景。
72 7
深度强化学习中SAC算法:数学原理、网络架构及其PyTorch实现
|
11天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
36 12
|
11天前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
37 10
|
26天前
|
算法 Java 数据库
理解CAS算法原理
CAS(Compare and Swap,比较并交换)是一种无锁算法,用于实现多线程环境下的原子操作。它通过比较内存中的值与预期值是否相同来决定是否进行更新。JDK 5引入了基于CAS的乐观锁机制,替代了传统的synchronized独占锁,提升了并发性能。然而,CAS存在ABA问题、循环时间长开销大和只能保证单个共享变量原子性等缺点。为解决这些问题,可以使用版本号机制、合并多个变量或引入pause指令优化CPU执行效率。CAS广泛应用于JDK的原子类中,如AtomicInteger.incrementAndGet(),利用底层Unsafe库实现高效的无锁自增操作。
理解CAS算法原理
|
11天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
36 2
|
26天前
|
存储 算法 Python
文件管理系统中基于 Python 语言的二叉树查找算法探秘
在数字化时代,文件管理系统至关重要。本文探讨了二叉树查找算法在文件管理中的应用,并通过Python代码展示了其实现过程。二叉树是一种非线性数据结构,每个节点最多有两个子节点。通过文件名的字典序构建和查找二叉树,能高效地管理和检索文件。相较于顺序查找,二叉树查找每次比较可排除一半子树,极大提升了查找效率,尤其适用于海量文件管理。Python代码示例包括定义节点类、插入和查找函数,展示了如何快速定位目标文件。二叉树查找算法为文件管理系统的优化提供了有效途径。
50 5
|
25天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
1月前
|
存储 人工智能 缓存
【AI系统】布局转换原理与算法
数据布局转换技术通过优化内存中数据的排布,提升程序执行效率,特别是对于缓存性能的影响显著。本文介绍了数据在内存中的排布方式,包括内存对齐、大小端存储等概念,并详细探讨了张量数据在内存中的排布,如行优先与列优先排布,以及在深度学习中常见的NCHW与NHWC两种数据布局方式。这些布局方式的选择直接影响到程序的性能,尤其是在GPU和CPU上的表现。此外,还讨论了连续与非连续张量的概念及其对性能的影响。
70 3
|
2月前
|
机器学习/深度学习 人工智能 算法
探索人工智能中的强化学习:原理、算法与应用
探索人工智能中的强化学习:原理、算法与应用
|
2月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
112 4

热门文章

最新文章