当前位置: 首页 > 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;系统会自动报警。…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...