搜索树基础:二叉搜索树(详解特性用途,图解实现过程)

news2025/1/14 0:44:45

二叉搜索树

  • 二叉搜索树的特性
  • 二叉搜索树的主要用途
  • 二叉搜索树的基本操作
    • 1、二叉搜索树的查找
    • 2、二叉搜索树的插入
    • 3、二叉搜索树的删除(难点)
      • (1)找到待删结点
      • (2)分情况删除

二叉搜索树的特性

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

二叉搜索树的主要用途

二叉搜索树主要用于实现高效的数据查找和排序:

  1. 查找:由于二叉搜索树的特性,可以通过比较节点的键值来快速定位目标节点。在查找操作中,对于一颗相对平衡的二叉搜索树,每次都可以将搜索范围缩小一半,因此平均时间复杂度为O(log n),其中n是树中节点的数量。
  2. 排序:对于一个无序的序列,可以利用二叉搜索树的性质进行排序。具体实现方法是,将序列中的元素依次插入到二叉搜索树中,然后进行中序遍历,即可按照升序输出有序序列。这种基于二叉搜索树的排序算法,对于一颗相对平衡的二叉搜索树,平均时间复杂度为O(nlog n)

在这里插入图片描述

需要注意的是,二叉搜索树的性能取决于树的形状。如果构建出的二叉搜索树高度不平衡(只有左树或只有右树),查找的时间复杂度可能达到O(n),导致操作效率会大幅下降。因此,在实际使用中,需要注意维护二叉搜索树的平衡性,以提高操作效率,在此基础上演化出了 AVL树、红黑树等更具平衡性的数据结构。

二叉搜索树是学习其他“搜索”数据结构的基础,下面就围绕二叉搜索树的一些操作进行展开介绍:

二叉搜索树的基本操作

为了实现二叉搜索树的相关操作,这里首先给出二叉搜索树节点的定义:

static class TreeNode {
 	 public int val;
     public TreeNode left;
     public TreeNode right;

     public TreeNode(int val) {
         this.val = val;
     }
 }
// 二叉搜索树根节点,初始时为空
public TreeNode root = null;

1、二叉搜索树的查找

实现思路(利用二叉搜索树的特性):

  1. 从根节点开始,与目标键值进行比较。
  2. 如果目标键值等于当前节点的键值,则找到了目标节点;
  3. 如果目标键值小于当前节点的键值,则继续在左子树中查找;
  4. 如果目标键值大于当前节点的键值,则继续在右子树中查找;
  5. 重复步骤2、3、4直到找到目标节点或者遍历到叶子节点。
public TreeNode find(int val) {
    // 从根节点开始查找
    TreeNode cur = root;
    while (cur != null) {
        if (cur.val == val) {
            // 找到返回结点
            return cur;
        } else if (cur.val > val) {
            // 当前结点值大于目标结点
            cur = cur.left;
        } else {
            // 当前结点小于目标结点
            cur = cur.right;
        }
    }
    // 找不到
    return null;
}

2、二叉搜索树的插入


经过简单分析,当搜索树非空时,二叉树的插入,总是插入到相应的叶子结点(由于结点之间单向联通,所以需要记录待插入位置的父节点):

  1. 从根节点开始,与要插入的键值进行比较。
  2. 如果要插入的键值小于当前节点的键值并且当前节点的左子节点为空,则将新节点作为当前节点的左子节点;
  3. 如果要插入的键值大于当前节点的键值并且当前节点的右子节点为空,则将新节点作为当前节点的右子节点。
public boolean insert(int val) {
    // 如果当前根节点为空,则插入到根节点
    if (root == null) {
        root = new TreeNode(val);
        return true;
    }
    TreeNode cur = root;
    // 记录插入位置的父节点
    TreeNode parent = null;
    // 寻找插入位置
    while (cur != null) {
        if (cur.val > val) {
        	// 当前结点值大于目标结点
            parent = cur;
            cur = cur.left;
        } else if (cur.val < val) {
        	// 当前结点值小于目标结点
            parent = cur;
            cur = cur.right;
        } else {
            // 如果二叉搜索树中已存在,直接返回
            System.out.println("已存在:" + val);
            return false;
        }
    }
    // 构造插入结点
    TreeNode node = new TreeNode(val);
    // 此时 parent 结点指向插入位置的父节点
    if (parent.val > val) {
        parent.left = node;
    } else {
        parent.right = node;
    }
    return true;
}

3、二叉搜索树的删除(难点)

(1)找到待删结点

二叉搜索树的删除,首先需要找到待删除结点,然后执行删除操作(由于结点之间单向联通,所以需要记录待删结点的父节点):

public void remove(int val) {
	// 待删结点,从root开始找
    TreeNode cur = root;
    // 待删结点的父节点
    TreeNode parent = null;
    while (cur != null) {
        if (cur.val == val) {
        	// 找到目标结点,调用删除操作
            removeNode(cur, parent);
            return;
        } else if (cur.val > val) {
        	// 当前结点值大于目标结点
            parent = cur;
            cur = cur.left;
        } else {
        	// 当前结点值小于目标结点
            parent = cur;
            cur = cur.right;
        }
    }
    System.out.println("不存在:" + val);
}

(2)分情况删除

二叉搜索树的删除操作,是一个相对复杂的操作,需要考虑多种不同情况:

设待删除结点为 cur, 待删除结点的双亲结点为 parent

cur.left == null

思路

  1. cur 是 root,则 root = cur.right
  2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
  3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

cur.right == null

思路:

  1. cur 是 root,则 root = cur.left
  2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
  3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.left

cur.left != null && cur.right != null

思路

此时需要使用替换法进行删除。可以选择用其左子树cur.left的最大值节点,或右子树cur.right的最小值节点的值填补到被删除节点中,然后删除这个最大或最小值节点。

上面这两种选哪个都行,下面我就以选择右子树 cur.right 中的最小值结点替换删除为例:

  • 当右子树的根节点有左孩子,那么 minParent.left 是右子树的最小值

  • 当右树根结点没有左孩子,那么 minParent.right 是右子树的最小值

最终实现代码:

private void removeNode(TreeNode cur, TreeNode parent) {
    if (cur.left == null) {
        // 1.如果待删除结点左孩子为空
        if (cur == root) {
            //左孩子为空的前提下,cur为根节点
            cur = cur.right;
        } else if (parent.left == cur) {
            //左孩子为空的前提下,cur为父节点的左树
            parent.left = cur.right;
        } else {
            //左孩子为空的前提下,cur为父节点的右树
            parent.right = cur.right;
        }
    } else if (cur.right == null) {
        // 2.如果待删除结点右孩子为空
        if (cur == root) {
            //右孩子为空的前提下,cur为根节点
            cur = cur.left;
        } else if (parent.left == cur) {
            //右孩子为空的前提下,cur为父节点的左树
            parent.left = cur.left;
        } else {
            //右孩子为空的前提下,cur为父节点的右树
            parent.right = cur.left;
        }
    } else {
        // 3.如果待删除结点左右孩子都不为空

        //这里是cur.left!=null && cur.right!=null的情况
        //替换删除:(找到右树里的最小值替换删除/找到左树的最大值替换删除)
        //下面展示找到右树里的最小值替换删除
        TreeNode minParent = cur;
        TreeNode min = cur.right;
        while (min.left != null) {
            minParent = min;
            min = min.left;
        }
        //此时 min 的左边一定为空
        //值替换
        cur.val = min.val;
        if (minParent.right == min) {
            // 当右树根结点没有左孩子,那么 minParent.right 是右子树的最小值
            minParent.right = min.right;
        } else {
            // 当右子树的根节点有左孩子,那么 minParent.left 是右子树的最小值
            minParent.left = min.right;
        }
    }
}

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

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

相关文章

如何高效沟通避免沟通冲突 ?2大沟通漏斗模型

“昨天&#xff0c;我不是说这个Bug了&#xff1f;”“没有啊&#xff0c;你啥时候说的&#xff1f;”项目组成员沟通效率不高&#xff0c;往往造成沟通误解、争论不休&#xff0c;甚至出现沟通冲突&#xff0c;影响项目进度。 那么该如何提高沟通效率&#xff0c;避免不必要的…

Fmoc-D-Ser(Ac4-L-Manα)-OH主要采用的是Fmoc合成法,Fmoc合成法是以Fmoc为α-氨基的保护基

Fmoc-D-Ser(Ac4-L-Manα)-OH主要采用的是Fmoc合成法。Fmoc合成法是以Fmoc为α-氨基的保护基&#xff0c;配合侧链保护的苄醇类&#xff0c;完成多肽合成。 在西安凯新生物科技有限公司的Fmoc合成法的实施过程中&#xff0c;首先将一个Fmoc&#xff0d;氨基酸衍生物共价交联到树…

老师们如何轻松制作学生分班查询系统

当暑假即将结束&#xff0c;老师们面临着许多事务&#xff0c;其中一个重要的任务是制作分班信息查询系统。这个工具对于老师们来说非常重要&#xff0c;因为它可以提供给家长和学生们方便快捷的查询服务。 分班信息查询系统可以让家长和学生们通过系统查询自己所在的班级、同班…

如何快捷发布学生分班情况

暑假即将结束&#xff0c;老师们面临着许多事务&#xff0c;其中一个重要的任务是制作分班信息查询系统。这个工具对于老师们来说非常重要&#xff0c;因为它可以提供给家长和学生们方便快捷的查询服务。 分班信息查询系统可以让家长和学生们通过系统查询自己所在的班级、同班同…

MySQL每日一练--校园教务系统

一丶数据库名称&#xff1a;SchoolDB 二丶数据库表信息&#xff1a;角色信息表 表名&#xff1a; t_role 主键&#xff1a; r_id 序号 字段名称 字段说明 类别 位数 属性 备注 1 r_id 角色编号 int 主键 自动增长 2 r_name_EN 角色名&#xff08;英文…

CPU内部单总线方式——例题分析

指令&#xff1a;ADD &#xff08;R0&#xff09;,R1 &#xff08;R0&#xff09; 目的操作数 &#xff08;R0&#xff09;说明R0中存放的是目的操作数的地址&#xff0c;则需要一次间接寻址 R1 源操作数 直接存在了R1寄存器中 最后&#xff08;R0&#xff09;存入R0所指向的存…

能源行业配电柜监控,这也太智能了!

在能源行业中&#xff0c;配电柜作为能量传输和控制的关键枢纽&#xff0c;其稳定运行直接关系到供电系统的可靠性和安全性。随着技术的不断进步&#xff0c;配电柜监控系统的发展为能源管理带来了全新的可能性。 配电柜监控系统的引入为能源行业带来了更高的智能化水平和管理效…

成集云 | 旺店通多包裹数据同步钉钉 | 解决方案

源系统成集云目标系统 方案介绍 随着品牌电商兴起&#xff0c;线上线下开始逐渐融为一体&#xff0c;成集云以旺店通ERP系统为例&#xff0c;通过成集云-旺店通连接器&#xff0c;将旺店通ERP系统多包裹数据同步至钉钉实现数据互通&#xff0c;帮助企业解决了电商发货存在的错…

面向对象知识点

一.Static 1.static修饰成员变量 被static修饰的成员变量&#xff0c;也叫做类变量&#xff0c;与类一同加载&#xff0c;只加载一次&#xff0c;被当前类的所有对象共享&#xff0c;只有一个 可以通过类名.属性的方法调用&#xff0c;也可以使用对象.属性的方式调用 应用场…

备份集中的数据库备份与现有的数据库不同?

数据已经成为公司的主要资产,特别是对于企业来说&#xff0c;数据库中存储的信息通常是其业务运营的核心。 因此&#xff0c;确保数据库的安全性和完整性至关重要。这导致数据库备份成为企业信息管理的重要组成部分。本文将详细介绍备份密集数据库备份的必要性&#xff0c;以及…

【python】Leetcode(primer-pointer)

文章目录 26. 删除有序数组中的重复项&#xff08;快慢指针&#xff09;88. 合并两个有序数组&#xff08;双指针&#xff09;167. 两数之和 II - 输入有序数组&#xff08;双指针&#xff09; 更多 leetcode 题解可参考&#xff1a;【Programming】 26. 删除有序数组中的重复项…

【linux内核】DP83867添加GMII模式支持

文章目录 修改方案前期知识为什么这么修改&#xff1f;通用寄存器 修改方案 linux 4.0内核下/drivers/net/phy/dp83867.c phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val);} if (phydev->interface PHY_INTERFACE_MODE_GMII){val phy_read_mmd(phydev, DP838…

CLIP(Contrastive Language-Image Pre-training)

《Learning Transferable Visual Models From Natural Language Supervision》 从自然语言监督中学习可迁移的视觉模型 贡献:利用自然语言信号监督,打破了固定类别的范式。 方法简单,效果好。从文本中得到监督信号,引导视觉分类的任务。 它是一个 zero-shot 的视觉分类…

深度研究:ASEMI快恢复二极管APT60DQ20BG

编辑-Z 在众多电子元件中&#xff0c;快恢复二极管APT60DQ20BG因其独特属性和广泛应用&#xff0c;吸引了广大电子爱好者和工程师的目光。本文将为您进行全面、深入的剖析。 首先&#xff0c;我们需要理解APT60DQ20BG是什么。APT60DQ20BG是一种快恢复二极管。二极管作为一个电…

【MySQL系列】Select语句单表查询详解(一)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

搭建AI智能问答的这些前期工作可不要忘了

在搭建AI智能问答之前&#xff0c;我们需要做好相应的前期准备工作&#xff0c;不能盲目地去搭建模型。这样很容易导致我们模型后续的不完整性。所以looklook今天就基于搭建AI智能问答最基础的思路&#xff0c;带领大家一起去理顺一下我们需要做什么前期工作才能保证AI智能问答…

【数据结构】C语言实现栈(详细解读)

前言: &#x1f4a5;&#x1f388;个人主页:​​​​​​Dream_Chaser&#xff5e; &#x1f388;&#x1f4a5; ✨✨专栏:http://t.csdn.cn/oXkBa ⛳⛳本篇内容:c语言数据结构--C语言实现栈 目录 什么是栈 栈的概念及结构 实现栈的方式 链表的优缺点: 顺序表的优缺点: 栈…

使用端口映射实现Spring Boot服务端接口的公网远程调试:详细配置与步骤解析

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…

【每日一招小技巧】编译安装提升系统自带的java版本

hello&#xff0c;大家好&#xff0c;又到了每日一招的环节&#xff0c;今天要给大家介绍的是&#xff0c;如何用源码安装的方式&#xff0c;升级系统自带的ava版本&#xff01; 首先&#xff0c;我们要查看一下我们系统自带的java版本是多少&#xff0c;怎么查看呢&#xff1…

『PyQt5-基础篇』| 02 Pyqt5开发环境+安装配置QtDesigner

02 Pyqt5开发环境安装配置QtDesigner 1 Pycharm安装2 Python安装3 Pip安装4 PyQt5安装5 Pycharm中编译工具设置及pyqt5包的导入6 指定Qt Designer7 指定PyUIC58 指定PyRcc59 PyInstaller安装10 查看是否配置OK 1 Pycharm安装 安装教程请参考&#xff1a;安装教程 2 Python安装…