当前位置: 首页 > news >正文

二叉搜索树(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个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多,但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log2​N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N/2

二叉搜索树退化,使二叉搜索树性能丢失,需要使二叉搜索树平衡,可以使用AVL树和红黑树。

相关文章:

二叉搜索树(Binary Search Tree)

1.二叉搜索树概念 二叉搜索树又称二叉排序树、二叉查找树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 1. 非空左子树的所有键值小于其根节点的键值 2. 非空右子树的所有键值大于其根节点的键值 3. 左右子树也分别为二叉搜索树 二叉搜索树一般不支持…...

Yii2框架的初始化及执行流程

当 Yii2 框架执行 index.php 入口脚本后&#xff0c;内部执行逻辑和顺序可以概括如下&#xff1a; 1、加载相关配置文件和关键组件&#xff1a; 加载 Composer 自动加载器&#xff1a; require DIR . ‘/…/vendor/autoload.php’; 加载 Yii 框架文件&#xff1a; require D…...

2024.1-2024.2pycharm无法打开terminal命令行

2024版的idea或pycharm打开terminal时会发生如下问题&#xff1a; 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期权移仓是什么&#xff1f;50ETF期权移仓要注意什么&#xff1f;当前火热的期权交易市场&#xff0c;“移仓”同样是一门非常重要的技术。上证50ETF期权投资的过程中&#xff0c;我们可以进行一定的移仓操作的&#xff0c;如果移仓操作得好&#xff0c;可以很…...

软件工程概述(上)

1、软件的概念、特点和分类 要了解软件工程&#xff0c;首先让我们重新认识一下软件。如今可以说是一个软件定义一切的时代&#xff0c;虽然人工智能发展的如火如荼&#xff0c;但究其本质&#xff0c;核心还是软件。那么&#xff0c;如何给软件下一个定义呢&#xff1f;软件又…...

阿里云ubuntu系统安装mysql8.0

一、安装mysql8.0 1.已安装其他版本的mysql&#xff0c;需要删除 若没有不需要此操作 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 背景 在某公司工作&#xff0c;向日葵等远程办公软件均已屏蔽&#xff0c;无法使用&#xff08;也没有明文规定不允许使用远程控制软件&#xff09;&#xff0c…...

数字资产是什么?怎么产生?怎么增长?

数字资产是什么&#xff1f; 数字资产是指企业或个人拥有或控制的&#xff0c;以电子数据形式存在的&#xff0c;在日常活动中持有以备出售或处于生产过程中的非货币性资产。它涵盖了广泛的范围&#xff0c;包括但不限于数字货币、数字证券、数字艺术品、虚拟土地等。这些资产…...

Centos7升级gitlab(17)

在 CentOS 7 中将 GitLab 从版本 17.1.1 升级到 17.2.2&#xff0c;涉及以下步骤。请务必在升级前备份数据&#xff0c;以防止升级过程中出现问题导致数据丢失。 升级步骤 1. 备份 GitLab 数据 在升级之前&#xff0c;确保已经备份了 GitLab 的数据&#xff0c;包括数据库、…...

Zookeeper详解以及常见的高可用关联组件

一、ZooKeeper 详解 Apache ZooKeeper 是一个开源的分布式协调服务&#xff0c;用于分布式应用程序之间的协调和管理。ZooKeeper 提供了一个高效、可靠的服务来帮助管理分布式系统中的共享配置信息、命名、同步和组服务等。 二、主要特性 1. 高可用性 ZooKeeper 集群通过选…...

Docker Containerd初体验

Docker Containerd概述 ​ Containerd是一个开源的容器运行时&#xff0c;它提供了一种标准化的方式来管理容器的生命周期。该项目最初是由Docker开发团队创建的&#xff0c;并在后来成为了一个独立的项目&#xff0c;被纳入了Cloud Native Computing Foundation&#xff08;C…...

开始使用 AWS SAM CLI

了解如何使用 AWS SAM CLI 在本地调试 lambda 函数 欢迎来到雲闪世界。我们将学习 AWS SAM CLI 的概念。SAM 是无服务器 应用程序 模型的缩写&#xff0c;是 Amazon Web Services 提供的一个框架&#xff0c;可以利用它在本地机器上构建应用程序并将其直接部署到 AWS Lambdas。…...

RK3588 RTL8125BG调试

RTL8125B是一款PCIE转RJ45的网卡控制器芯片&#xff0c;在底层调试时只需配置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 自省&#xff08;Introspection&#xff09;是一种强大的特性&#xff0c;它允许程序在运行时检查对象的类型、属性以及它们如何相互关联。这种能力让 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 登录界面&#xff0c;校验成功后可以登录到Main/Index ,用户登录3分钟内关闭网站&#xff0c;再次访问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…...

每天一个数据分析题(四百八十五)- 统计推断

假设检验中&#xff0c;关于p值说法正确的是&#xff1f; A. p值是在原假设成立时&#xff0c;样本观察结果发生的概率。 B. p值是接受原假设的概率 C. p值是相对样本统计量而言的 D. 用p值做决策比用域值做决策更准确 数据分析认证考试介绍&#xff1a;点击进入 题目来源…...

基于STM32的农业病虫害检测检测系统:OpenCV、MQTT、Flask框架、MySQL(代码示例)

一、项目概述 随着全球农业现代化的不断推进&#xff0c;智能农业监测系统应运而生。此项目旨在通过实时监测土壤湿度、温度等环境数据&#xff0c;结合作物生长状态的分析&#xff0c;提高农业生产效率和作物质量。通过引入STM32单片机、OpenCV图像处理技术和后端数据分析系统…...

算法日记day 39(动归之打家劫舍)

一、打家劫舍1 题目&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...