二叉搜索树(BST,Binary Search Tree)

目录

前言

一、二叉搜索树概念

二、二叉搜索树的实现与操作

1.查找

2.插入

3.删除

4.中序遍历 

5.完整代码 

三、二叉搜索树的应用(K模型、KV模型)

1.K模型

2.KV模型

3.完整代码

四、二叉搜索树的性能分析


前言

为何学?

1.二叉搜索树是一种树形结构,是一种查找效率非常高的结构,值得我们去学习

2.map和set的底层也是二叉搜素树,学习二叉搜索树可以让我们更好的了解set和map的特性

一、二叉搜索树概念

二叉搜索树(BST,Binary Search Tree)又称二叉排序树、二叉查找树

它可以是一颗空树

如果不是,它(或者说它的任何一颗子树)必须满足下列条件:

1.如果它的左子树不为空,则左子树上的所有节点值都小于根节点的值

2.如果它的右子树不为空,则右子树上的所有节点值都大于根节点的值

3.左、右子树都是二叉搜索树 

二、二叉搜索树的实现与操作

节点结构

template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};

树类

template<class K, class V>
class BSTree
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key){}bool Find(const K& key){}bool Erase(const K& key){}void _InOrder(Node* root){}void InOrder(){_InOrder(root);}
private:Node* root = nullptr;
};

 

1.查找

从根开始查找比较,比当前根值大往右边查找,比当前根值大往右边查找,且最多查找高度次,走到空还未找到,则所查找值不存在

代码实现:

bool Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;
}

2.插入

1.如果为空树,则将创建新节点,并将新节点赋值给根节点(root指针)

2.如果树不为空,则按二叉搜索树的性质先查找插入位置,再插入

bool Insert(const K& key)
{if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

3.删除

在二叉搜索树中删除是一个稍显麻烦的事,其中包含了许多细节

我们可以知道,待删除的节点有四种情景:

1.要删除的结点无孩子结点

2.要删除的结点只有左孩子结点

3.要删除的结点只有右孩子结点

4.要删除的结点有左、右孩子结点

 在后面的处理中1情景和23情景可以融合,因此去掉1情景,我们有3种情景

  • 情景2:使待删除节点的父节点指向待删除节点的左孩子,删除待删除节点
  • 情景3:使待删除节点的父节点指向待删除节点的右孩子,删除待删除节点 
  • 情景4:

这里我们事用替换法删除--即在待删除节点的子树中找一个合适的替换节点,用它的值替换填补调待删除节点,可以找左子树的最大值或者右子树的最小值 ,在处理替换节点的子树问题

下面的演示代码找的树右子树的最小值替换:

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{// 替换法Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;return true;}}}return false;
}

4.中序遍历 

既然二叉搜索树能叫做二叉排序树,自然是有原因的,在二叉搜索树中,对二叉搜索树来一次中序遍历,即可得到一组有序元素

 在C++的类中,不能直接调用递归函数,需要嵌套一层递归

void InOrder()
{_InOrder(_root);cout << endl;
}
void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}

5.完整代码 

template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{// 替换法Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;
};

三、二叉搜索树的应用(K模型、KV模型)

1.K模型

K模型中只有一个key值作为关键码,只存储key值 

如我们要在一个词库中搜索一个单词 “key”,我们只需要以这个词库的每一个单词作为key值构建二叉搜索树,再在这个树中查找key值为“key”的节点

2.KV模型

KV模型中的每一个key值都有一个对应的value值(也叫映射),即<Key,Value>键值对

如我们平时学习上经常用到的英汉词典,每个英文都对应着中文,英文单词与其对应的<Word,Chinese>就构成了一种键值对

下面就是上述K模型代码改造的KV模型代码

3.完整代码

template<class K, class V>
struct BSTreeNode
{typedef BSTreeNode<K, V> Node;Node* _left;Node* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}
};template<class K, class V>
class BSTree
{typedef BSTreeNode<K, V> Node;
public:bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return nullptr;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_right){parent->_right = cur->_left;}else{parent->_left = cur->_left;}}delete cur;return true;}else{// 替换法Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;
};

四、二叉搜索树的性能分析

根据上面的介绍我们可以知道,无论是删除还是插入,都需要先查找到需要处理的位置,因此,查找效率代表了二叉搜索树的各个操作额性能

对于一个有n个节点的二叉搜索树,查找的平均长度差不多就是二叉搜索树的深度,节点越深,次数越多。

插入次序不同,得到的二叉搜索树的结构可能不同,比如下图:

对于较好的情况参考上面的左图,二叉树为完全二叉树或者说接近完全二叉树,查找的平均次数为log n 

对于较差的情况参考上面的右图,二叉搜索树退化成单只树或者说接近单只树,查找的平均次数为n

若是退化为单只树,二叉搜索树就失去了它的性能优势

要想二叉搜索树的性能不管怎样插入都能达到最优,请继续学习后面大佬们发明的AVL树和红黑树,在那里你可以找到所要的答案

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

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

相关文章

我的python管理

目前环境 Anaconda&#xff1a;python3.9 python2.7 IDA&#xff1a;python3.8 pycharm&#xff1a;&#xff1f;&#xff1f; 以后应该会补吧… 因为某些文件似乎用的python2决定整个python2 安装python2.7 打开anaconda命令行输入 conda create --name python27 python2…

JAVAEE值网络编程(2)_TCP流套接字及通信模型、TCP网络编程及代码实例

前言 在上一节内容中&#xff0c;我们介绍了什么是套接字&#xff0c;以及使用UDP数据报套接字网络编程&#xff0c; 最后我们还介绍了Java数据报套接字通信模型以及相关代码实例。在这一节我们将会介绍TCP流套接字编程。 一、流套接字及通信模型 1.1 TCP套接字 TCP&#xff0…

云计算-高级云资源配置(Advanced Cloud Provisioning)

向Bucket添加公共访问&#xff08;Adding Public Access to Bucket&#xff09; 在模块5中&#xff0c;我们已经看到如何使用CloudFormation创建和更新一个Bucket。现在我们将进一步更新该Bucket&#xff0c;添加公共访问权限。我们在模块5中使用的模板&#xff08;third_templ…

内网安全--隧道技术代理技术

注:本文仅做技术交流,请勿非法破坏... 目录 项目: 1-Ngrok 用法 2-Frp 用法 3-Nps 用法 4-Spp 用法 工具: windows下: Proxifier(推荐~) Sockscap ccproxy Linux下: Proxychains 用法 http://t.csdnimg.cn/88Ew7 隧道技术&#xff1a;解决不出网协议上线的问…

TikTok运营必看|7大广告类型及特点

TikTok广告是品牌或创作者付费向特定目标受众展示的推广内容&#xff08;通常是全屏视频&#xff09;。TikTok 上的广告是一种社交媒体营销形式&#xff0c;通常旨在提高广告商的知名度或销售特定产品或服务。 就 TikTok广告投放而言&#xff0c;其组织层级分为三个层级&#x…

【Java】static 修饰成员方法

static 修饰成员方法 简介 应用 static 修饰成员方法 1.static 修饰成员方法2.内存原理3.main函数4.类方法的应用 1.static 修饰成员方法 测试类&#xff1a; package suziguang_d2_staticdemo;public class Test {public static void main(String[] args) {// 1.类方法使用/…

【成品设计】基于USB接口的指纹图像采集与处理系统设计

《基于USB接口的指纹图像采集与处理系统设计》 所需器件&#xff1a; STM32F429阿波罗开发板。ATK-AS608 模块指纹识别模块。USB转TTL模块。 整体功能&#xff1a; 实现指纹的采集录入。实现指纹的对比&#xff0c;并展示对比结果&#xff0c;用LED灯和蜂鸣器提示。指纹信息…

app自动识别ios或安卓手机,微信浏览器,并下载相应的apk安装包

来源是安卓下载界面显示: 来源是IOS下载界面显示: 源码 <!DOCTYPE html> <html lang="en"><head

微软新AI工具 Recall 被白帽公开锤了?

近日&#xff0c;一些网络安全研究人员演示了恶意软件是如何成功窃取 Windows Recall 工具收集到的数据。 2024年5月21日&#xff0c;微软发布全新的“CopilotPC”&#xff0c;这类 AI PC 通过与高通的最新芯片合作&#xff0c;实现了一个叫做“Recall”的功能。借助这个人工智…

5.31.8 学习深度特征以实现判别定位

1. 介绍 尽管没有对物体的位置提供监督,但卷积神经网络 (CNN) 各层的卷积单元实际上可以充当物体检测器。尽管卷积层具有这种出色的物体定位能力,但当使用全连接层进行分类时,这种能力就会丧失。最近,一些流行的全卷积神经网络,如 Network in Network (NIN) [13] 和 Goog…

Docker大学生看了都会系列(六、Dokcer容器数据卷)

系列文章目录 第一章 Docker介绍 第二章 2.1 Mac通过Homebrew安装Docker 第二章 2.2 CentOS安装Docker 第三章 Docker常用命令 第四章 常用命令实战 第五章 Docker镜像详解 第六章 Docker容器数据卷 文章目录 一、前言二、环境三、容器数据卷基本介绍3.1 什么是容器数据卷3.2 容…

nginx--centos安装

参考&#xff1a;https://blog.csdn.net/chang_chunhua/article/details/129298660 下载官网&#xff1a;https://nginx.org/ 选择稳定版本&#xff1a;stable 下载linux版本的&#xff1a; 可以选择直接下载到本地再上传到服务器相对应位置&#xff01; 同时也可以用下载地…

利用MaxKB+Ollama:搭建智能问答系统_Ubuntu部署maxkb

Docker方式&#xff0c;不建议使用 即使maxKB和ollama在同一目录下&#xff0c;API域名也显示无效。 Ollama下载网址&#xff1a;Download Ollama on Linux Linux下载&#xff1a;curl -fsSL https://ollama.com/install.sh | sh The Ollama API is now available at 127.0.…

openh264 自适应量化功能源码分析

openh264 OpenH264是一个开源的H.264/AVC视频编解码器&#xff0c;由Cisco公司发起并贡献了最初的代码基础。它提供了一个用于视频编码和解码的库&#xff0c;支持H.264视频压缩标准&#xff0c;广泛应用于视频会议、流媒体和视频存储等领域。OpenH264是实现H.264编解码功能的…

【鸿蒙】开发之页面跳转组件—实现页面跳转方法汇总!

①不同 Slice 间跳转&#xff0c;同一个 Ability 中&#xff0c;优点是方便&#xff0c;高效&#xff0c;缺点是业务逻辑复杂度受限。 button.setClickedListener(listener -> present(new SecondAbilitySlice(), new Intent()) );②使用 Intent 借助于 ElementName&#x…

javascript导入excel文件

导入文件用到一个 xlsx.core.js 的包。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><script type"tex…

DSP28335模块配置模板系列——ADC配置模板

一、配置步骤 1.使能并配置高速时钟HSPCLK、ADC校验 EALLOW;SysCtrlRegs.PCLKCR0.bit.ADCENCLK 1; EDIS;EALLOW;SysCtrlRegs.HISPCP.all ADC_MODCLK; // HSPCLK SYSCLKOUT/(2*ADC_MODCLK)ADC_cal();EDIS; 这里ADC_MODCLK3&#xff0c;所以HSPCLK时钟为150/625Mhz 2.配…

《TCP/IP网络编程》(第十三章)多种I/O函数(2)

使用readv和writev函数可以提高数据通信的效率&#xff0c;它们的功能可以概括为**“对数据进行整合传输及发送”**。 即使用writev函数可以将分散在多个缓冲中的数据一并发送&#xff0c;使用readv函数可以由多个缓冲分别接受&#xff0c;所以适当使用他们可以减少I/O函数的调…

压力测试-性能指标-Jmeter使用-压力测试报告

文章目录 1.压测目的2.性能指标3.Jmeter3.1Jmeter使用3.1.1 运行Jmeter3.1.2 添加线程组3.1.3设置HTTP请求3.1.4 设置监视器 3.2 查看Jmeter压测结果3.2.1 查看结果树3.2.2 查看汇总报告3.2.3 查看聚合报告3.2.4 查看汇总图 1.压测目的 内存泄漏&#xff1a;OOM&#xff0c;重…

Hadoop3:MapReduce源码解读之Map阶段的CombineFileInputFormat切片机制(4)

Job那块的断点代码截图省略&#xff0c;直接进入切片逻辑 参考&#xff1a;Hadoop3&#xff1a;MapReduce源码解读之Map阶段的Job任务提交流程&#xff08;1&#xff09; 6、CombineFileInputFormat原理解析 类的继承关系 与TextInputFormat切片机制的区别 框架默认的TextI…