二叉搜索树(Binary Search Tree)
1.二叉搜索树概念
二叉搜索树又称二叉排序树、二叉查找树,它或者是一棵空树,或者是具有以下性质的二叉树:
1. 非空左子树的所有键值小于其根节点的键值
2. 非空右子树的所有键值大于其根节点的键值
3. 左右子树也分别为二叉搜索树
二叉搜索树一般不支持key值冗余(不允许有重复的key值)
二叉搜索树的性质使搜索时非常方便高效,最多搜索高度次O(log N)(二叉树退化为O(N),需要二叉搜索树平衡,使用AVL树或红黑树)
二叉搜索树的中序遍历能对key进行排序
二叉搜索树的应用:
K模型:K模型只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值
例如查找某个key值存不存在?
KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对
key值不能冗余,但value值可以相同
例如key值为单词,value为对应单词的英文
2.二叉搜索树操作(K模型为例)

2.1 二叉搜索树的查找
1. 从根节点开始查找,查找的key比cur(当前节点)的key大,则cur向右查找;查找的key比cur的key小,则cur向左查找,直到找到key值相同或查找的key值不存在。
2. 最多查找高度次,cur走到空,就说明查找的key不存在。
Node* find(const T& data)
{Node* cur = _root;while (cur){//找到返回if (data == cur->_data)return cur;//大于cur,向右找else if (data > cur->_data)cur = cur->_right;//小于cur,向左找elsecur = cur->_left;}//没找到return nullptr;return nullptr;
}
2.2 二叉搜索树的插入
1. 树为空树时,则直接新增节点,并作为该对象的根节点(_root)。
2. 树不为空树时,按二叉树的性质查找位置并插入

bool insert(const T& data)
{//树为空if (_root == nullptr){_root = new Node(data);return true;}//树不为空Node* cur = _root;Node* parent = nullptr;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right;}else if (data < cur->_data){parent = cur;cur = cur->_left;}else//树中已经存在return false;}//插入cur = new Node(data);if (data > parent->_data)parent->_right = cur;elseparent->_left = cur;return true;
}
2.3 二叉搜索树的删除
查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的节点分四种情况:
1. 要删除的节点无孩子节点 -- 直接删除该节点
2. 要删除的节点只有左孩子节点 -- 让左孩子节点替代该节点位置,并删除该节点
3. 要删除的节点只有右孩子节点 -- 让右孩子节点替代该节点位置,并删除该节点
4. 要删除的节点有左右孩子节点 -- 由二叉树性质,查找删除节点的右子树key值最小节点,让最小节点的key覆盖掉删除节点的key,最小节点一定是只有一个右孩子节点或没有孩子节点的情况,按1或2删除最小节点。(最小节点不可能存在左孩子,不然就不是最小节点了,更不可能有两个孩子)
bool erase(const T& data)
{//空树,删除失败if (_root == nullptr)return false;Node* cur = _root;Node* parent = nullptr;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right;}else if (data < cur->_data){parent = cur;cur = cur->_left;}else//此时cur就是需要删除都节点break;}//不存在if (cur == nullptr)return false;//存在if (cur->_left == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_right;else{if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;return true;}else if (cur->_right == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_left;else{if (cur == parent->_left)parent->_left = cur->_left;else if (cur == parent->_right)parent->_right = cur->_left;}delete cur;return true;}//删除节点有两个孩子节点else{Node* minNode = cur->_right;Node* minNodeParent = cur;while (minNode->_left != nullptr){//最左就是最小节点minNodeParent = minNode;minNode = minNode->_left;}//修改_datacur->_data = minNode->_data;//删除minNodeif (minNodeParent->_left == minNode)minNodeParent->_left = minNode->_right;else//注意这个情况minNodeParent->_right = minNode->_right;delete minNode;return true;}
}
2.4 二叉搜索树代码(K模型和KV模型)
KV模型与K模型类似,只是KV模型的data类型是pair类型,first是key,second是value。
K模型:
namespace myBSTree_K
{template <class T>struct BSTNode{BSTNode(const T& data = T()):_left(nullptr), _right(nullptr), _data(data){}BSTNode<T>* _left;BSTNode<T>* _right;T _data;};template <class T>class BSTree{typedef BSTNode<T> Node;public:BSTree():_root(nullptr){}BSTree(const BSTree& tree){_root = _Copy(tree._root);}//析构函数无法传参,只能调用子函数进行递归销毁~BSTree(){//后序遍历销毁_destory(_root);_root = nullptr;}Node* find(const T& data){Node* cur = _root;while (cur){//找到返回if (data == cur->_data)return cur;//大于cur,向右找else if (data > cur->_data)cur = cur->_right;//小于cur,向左找elsecur = cur->_left;}//没找到return nullptr;return nullptr;}bool insert(const T& data){//树为空if (_root == nullptr){_root = new Node(data);return true;}//树不为空Node* cur = _root;Node* parent = nullptr;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right;}else if (data < cur->_data){parent = cur;cur = cur->_left;}else//树中已经存在return false;}//插入cur = new Node(data);if (data > parent->_data)parent->_right = cur;elseparent->_left = cur;return true;}bool erase(const T& data){//空树,删除失败if (_root == nullptr)return false;Node* cur = _root;Node* parent = nullptr;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right;}else if (data < cur->_data){parent = cur;cur = cur->_left;}else//此时cur就是需要删除都节点break;}//不存在if (cur == nullptr)return false;//存在if (cur->_left == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_right;else{if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;return true;}else if (cur->_right == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_left;else{if (cur == parent->_left)parent->_left = cur->_left;else if (cur == parent->_right)parent->_right = cur->_left;}delete cur;return true;}//删除节点有两个孩子节点else{Node* minNode = cur->_right;Node* minNodeParent = cur;while (minNode->_left != nullptr){//最左就是最小节点minNodeParent = minNode;minNode = minNode->_left;}//修改_datacur->_data = minNode->_data;//删除minNodeif (minNodeParent->_left == minNode)minNodeParent->_left = minNode->_right;else//注意这个情况minNodeParent->_right = minNode->_right;delete minNode;return true;}}void InOrder(){_InOrder(_root);}private:void _destory(Node* root){if (root == nullptr){return;}_destory(root->_left);_destory(root->_right);delete root;root = nullptr;}Node* _Copy(const Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_data);newRoot->_left = _Copy(root->_left);newRoot->_right = _Copy(root->_right);return newRoot;}//中序遍历void _InOrder(const Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_data << ' ';_InOrder(root->_right);}//成员变量Node* _root;};
}
KV模型:
namespace myBSTree_KV
{template <class K, class V>struct BSTNode{BSTNode(const pair<K,V>& data = pair<K,V>()):_left(nullptr), _right(nullptr), _data(data){}BSTNode<K,V>* _left;BSTNode<K,V>* _right;pair<K,V> _data;};template <class K, class V>class BSTree{typedef BSTNode<K,V> Node;public:BSTree():_root(nullptr){}BSTree(const BSTree& tree){_root = _Copy(tree._root);}//析构函数无法传参,只能调用子函数进行递归销毁~BSTree(){//后序遍历销毁_destory(_root);_root = nullptr;}Node* find(const K& key){Node* cur = _root;while (cur){//找到返回if (key == cur->_data.first)return cur;//大于cur,向右找else if (key > cur->_data.first)cur = cur->_right;//小于cur,向左找elsecur = cur->_left;}//没找到return nullptr;return nullptr;}bool insert(const pair<K, V>& data){//树为空if (_root == nullptr){_root = new Node(data);return true;}//树不为空Node* cur = _root;Node* parent = nullptr;while (cur){if (data.first > cur->_data.first){parent = cur;cur = cur->_right;}else if (data.first < cur->_data.first){parent = cur;cur = cur->_left;}else//树中已经存在return false;}//插入cur = new Node(data);if (data.first > parent->_data.first)parent->_right = cur;elseparent->_left = cur;return true;}bool erase(const K& key){//空树,删除失败if (_root == nullptr)return false;Node* cur = _root;Node* parent = nullptr;while (cur){if (key> cur->_data.first){parent = cur;cur = cur->_right;}else if (key < cur->_data.first){parent = cur;cur = cur->_left;}else//此时cur就是需要删除都节点break;}//不存在if (cur == nullptr)return false;//存在if (cur->_left == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_right;else{if (cur == parent->_left)parent->_left = cur->_right;elseparent->_right = cur->_right;}delete cur;return true;}else if (cur->_right == nullptr){//cur是根节点if (parent == nullptr)_root = cur->_left;else{if (cur == parent->_left)parent->_left = cur->_left;else if (cur == parent->_right)parent->_right = cur->_left;}delete cur;return true;}//删除节点有两个孩子节点else{Node* minNode = cur->_right;Node* minNodeParent = cur;while (minNode->_left != nullptr){//最左就是最小节点minNodeParent = minNode;minNode = minNode->_left;}//修改_datacur->_data = minNode->_data;//删除minNodeif (minNodeParent->_left == minNode)minNodeParent->_left = minNode->_right;else//注意这个情况minNodeParent->_right = minNode->_right;delete minNode;return true;}}//改--如果key相同,改value,如果key不存在,不改bool update(const pair<K, V>& data){Node* cur = find(data.first);if (cur == nullptr)return false;else{cur->_data.second = data.second;return true;}}void InOrder(){_InOrder(_root);}private:void _destory(Node* root){if (root == nullptr){return;}_destory(root->_left);_destory(root->_right);delete root;root = nullptr;}Node* _Copy(const Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_data);newRoot->_left = _Copy(root->_left);newRoot->_right = _Copy(root->_right);return newRoot;}//中序遍历void _InOrder(const Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_data.first << ':' << root->_data.second << endl;_InOrder(root->_right);}//成员变量Node* _root;};
}
3. 二叉搜索树的性能分析
插入和删除操作都必须先查找,查找效率代表二叉搜索树的各个操作的性能
对有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多,但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N/2
二叉搜索树退化,使二叉搜索树性能丢失,需要使二叉搜索树平衡,可以使用AVL树和红黑树。
相关文章:
二叉搜索树(Binary Search Tree)
1.二叉搜索树概念 二叉搜索树又称二叉排序树、二叉查找树,它或者是一棵空树,或者是具有以下性质的二叉树: 1. 非空左子树的所有键值小于其根节点的键值 2. 非空右子树的所有键值大于其根节点的键值 3. 左右子树也分别为二叉搜索树 二叉搜索树一般不支持…...
Yii2框架的初始化及执行流程
当 Yii2 框架执行 index.php 入口脚本后,内部执行逻辑和顺序可以概括如下: 1、加载相关配置文件和关键组件: 加载 Composer 自动加载器: require DIR . ‘/…/vendor/autoload.php’; 加载 Yii 框架文件: require D…...
2024.1-2024.2pycharm无法打开terminal命令行
2024版的idea或pycharm打开terminal时会发生如下问题: Cannot open Windows PowerShell Failed to start [C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe,或 Cannot open Command Prompt Failed to start [C:\Windows\system32\cmd.exe] 需要点击标…...
50ETF期权移仓是什么?50ETF期权移仓要注意什么?
今天带你了解50ETF期权移仓是什么?50ETF期权移仓要注意什么?当前火热的期权交易市场,“移仓”同样是一门非常重要的技术。上证50ETF期权投资的过程中,我们可以进行一定的移仓操作的,如果移仓操作得好,可以很…...
软件工程概述(上)
1、软件的概念、特点和分类 要了解软件工程,首先让我们重新认识一下软件。如今可以说是一个软件定义一切的时代,虽然人工智能发展的如火如荼,但究其本质,核心还是软件。那么,如何给软件下一个定义呢?软件又…...
阿里云ubuntu系统安装mysql8.0
一、安装mysql8.0 1.已安装其他版本的mysql,需要删除 若没有不需要此操作 1 #卸载MySQL5.7版本 2 apt remove -y mysql-client5.7* mysql-community-server5.7* 4 # 卸载5.7的仓库信息 5 dpkg-l | grep mysql | awk iprint $2} | xargs dpkg -P2.更新仓库 apt u…...
自己搭建远程桌面服务器-RustDesk 极简版
linux搭建RustDesk保姆间教程_rustdesk linux-CSDN博客https://blog.csdn.net/yzs2022/article/details/135136491 背景 在某公司工作,向日葵等远程办公软件均已屏蔽,无法使用(也没有明文规定不允许使用远程控制软件),…...
数字资产是什么?怎么产生?怎么增长?
数字资产是什么? 数字资产是指企业或个人拥有或控制的,以电子数据形式存在的,在日常活动中持有以备出售或处于生产过程中的非货币性资产。它涵盖了广泛的范围,包括但不限于数字货币、数字证券、数字艺术品、虚拟土地等。这些资产…...
Centos7升级gitlab(17)
在 CentOS 7 中将 GitLab 从版本 17.1.1 升级到 17.2.2,涉及以下步骤。请务必在升级前备份数据,以防止升级过程中出现问题导致数据丢失。 升级步骤 1. 备份 GitLab 数据 在升级之前,确保已经备份了 GitLab 的数据,包括数据库、…...
Zookeeper详解以及常见的高可用关联组件
一、ZooKeeper 详解 Apache ZooKeeper 是一个开源的分布式协调服务,用于分布式应用程序之间的协调和管理。ZooKeeper 提供了一个高效、可靠的服务来帮助管理分布式系统中的共享配置信息、命名、同步和组服务等。 二、主要特性 1. 高可用性 ZooKeeper 集群通过选…...
Docker Containerd初体验
Docker Containerd概述 Containerd是一个开源的容器运行时,它提供了一种标准化的方式来管理容器的生命周期。该项目最初是由Docker开发团队创建的,并在后来成为了一个独立的项目,被纳入了Cloud Native Computing Foundation(C…...
开始使用 AWS SAM CLI
了解如何使用 AWS SAM CLI 在本地调试 lambda 函数 欢迎来到雲闪世界。我们将学习 AWS SAM CLI 的概念。SAM 是无服务器 应用程序 模型的缩写,是 Amazon Web Services 提供的一个框架,可以利用它在本地机器上构建应用程序并将其直接部署到 AWS Lambdas。…...
RK3588 RTL8125BG调试
RTL8125B是一款PCIE转RJ45的网卡控制器芯片,在底层调试时只需配置PCIE即可 diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi index 798359eaf061..d8a7a43cdfa0 100755 --- a/arch/arm64/bo…...
Python自省(机制与函数)
Python 自省(Introspection)是一种强大的特性,它允许程序在运行时检查对象的类型、属性以及它们如何相互关联。这种能力让 Python 非常适合于快速开发、调试以及编写需要高度动态交互的代码。Python 的自省机制主要通过内置的函数和类型来实现…...
【JavaEE】JVM 内存区域划分,以及 Java 垃圾回收机制引用计数器,可达性分析等
目录 1. JVM执行流程 2. JVM运行时数据区 2.1 堆 2.2 Java虚拟机栈(线程私有) 2.3本地方法栈(线程私有) 2.4 程序计数器 2.5 元数据区 3. JVM的类加载机制 1) 加载 2) 验证 3) 准备 4) 解析 5) 初始化 双亲委派模型 4. java垃圾回收 4.1 死亡对象判断方法 a) …...
Web开发:C# MVC + Session机制实现授权免登录demo
token基础demo 【需求】 Home/Index 登录界面,校验成功后可以登录到Main/Index ,用户登录3分钟内关闭网站,再次访问Home/Index时可以免密登录Main/Index 【配置文件-Program.cs】 var builder WebApplication.CreateBuilder(args);// Add services t…...
【Qt】QWidget的font属性
QWidget的font属性 API说明 font() 获取当前 widget 的字体信息. 返回 QFont 对象. setFont(const QFont& font) 设置当前 widget 的字体信息. 关于Qfont 属性说明 family 字体家族. ⽐如 "楷体", "宋体", "微软雅⿊" 等. pointSiz…...
每天一个数据分析题(四百八十五)- 统计推断
假设检验中,关于p值说法正确的是? A. p值是在原假设成立时,样本观察结果发生的概率。 B. p值是接受原假设的概率 C. p值是相对样本统计量而言的 D. 用p值做决策比用域值做决策更准确 数据分析认证考试介绍:点击进入 题目来源…...
基于STM32的农业病虫害检测检测系统:OpenCV、MQTT、Flask框架、MySQL(代码示例)
一、项目概述 随着全球农业现代化的不断推进,智能农业监测系统应运而生。此项目旨在通过实时监测土壤湿度、温度等环境数据,结合作物生长状态的分析,提高农业生产效率和作物质量。通过引入STM32单片机、OpenCV图像处理技术和后端数据分析系统…...
算法日记day 39(动归之打家劫舍)
一、打家劫舍1 题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
