C++进阶(3): 二叉搜索树

二叉搜索树的概念

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

  • 若它的左子树不为空,则左子树上所有的节点的值都小于等于 根节点的值
  • 若它的右子树不为空,则右子树上所有的节点的值都大于等于 根节点的值
  • 它的左右子树也分别为二叉搜索树
  • 二叉搜索树可以支持插入相同的值(multimap/multiset),也可以不支持插入相等的值(map/set)

二叉搜索树的性能分析

  1. 最优的情况:二叉搜索树为完全二叉树(或者接近完全二叉树),它的高度是:O(log2 N)
  2. 最坏的情况:二叉搜索树退化为单支树(类似于单支),它的高度是:O(N/2)

所以综合而言二叉搜索树增删改时间复杂度是:O(N)
但是这样的效率显然无法满足我们的需求,后续的博客我将介绍二叉搜索树的变形,平衡二叉树AVL树和红黑树,才能适用于我们在内存在存储和搜索数据。

最好的情况
最坏的情况

二叉搜索树的插入

不用递归,采用更简单的循环

插入的具体步骤如下:

  1. 树为空,则直接新增节点,赋值给root指针
  2. 树不为空,按照二叉搜索树性质,插入值比当前节点大往右走,插入值比当前节点小往左走,找到空位置,插入新节点。
  3. 如果支持插入相同的值,插入值跟当前节点相等的值可以往右走,也可以往走左走,找到空位置,插入新节点(要注意保持逻辑的一致性,插入相同的值不要一会向右走,一会向左走)

二叉搜索树的查找

  1. 从根节点开始比较,查找x,x比根节点的值大则向右边走查找,x比根节点的值小则向左边走查找
  2. 最多查找高度次,走到空时,还没找到x,那么这个值在这个二叉搜索树中不存在
  3. 如果不支持插入相同的值,则找到x后可以直接返回
  4. 如果支持插入相同的值,则意味着可能存在多个x,一般要求查找中序的第一个x

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false
如果查找元素存在则分为以下四个情况进行分别处理:(假设要删除的节点为N)(分类依据:孩子的个数)

  1. 要删除的节点N左右孩子均为空(即N为叶子节点)
  2. 要删除的节点N左孩子为空,右孩子不为空
  3. 要删除的节点N左孩子不为空,右孩子为空
  4. 要删除的节点N左右孩子均不为空

对于以上四种情况的解决办法:

  1. 把N节点的父亲对应孩子指针指向空,直接删除N节点(1可以和2.3一起处理)
  2. 把N节点的父亲对应孩子指针指向N的右孩子,直接删除N节点
  3. 把N节点的父亲对应孩子指针指向N的左孩子,直接删除N节点
  4. 无法直接删除N节点,因为N的两个孩子无处安放,只能采用替代法将其删除:将N左子树的最大值的节点R(最右节点)或者N右子树的最小值的节点R(最左节点)代替N,因为这两个节点中任意一个,放到N的节点,都满足二叉搜索树的规则。代替N的意思就是N和R的两个节点的值交换,转而变成删除R节点,R节点符合情况2或者情况3,可以直接删除
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

实现代码

template<class K>struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}
};//搜索二叉树:
template<class K>class BSTree
{typedef BSTNode<K> Node;public: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->_left = cur;}else{parent->_right = cur;}return true;}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{if (nullptr == cur->_left){if (parent == nullptr){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else if (parent->_right = cur){parent->_right = cur->_right;}}delete cur;return true;}else if (nullptr == cur->_right){if (nullptr == parent){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}else{Node* leftMaxP = cur;Node* leftMax = cur->_left;while (leftMax->right){leftMaxP = leftMax;leftMax = leftMax->_right;}cur->_key = leftMax->_key;if (leftMaxP->_left == leftMax){leftMaxP->_left = leftMax->_left;}else{leftMaxP->_right = leftMax->_left;}delete leftMax;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;
};

二叉搜索树key和key/value使用场景

key搜索场景

只有key作为关键码,在二叉树结构中只需要存储key即可,搜索场景只需要判断key在不在。key的搜索场景支持二叉树的增删查,而不支持二叉树的修改,会破坏二叉树的结构。

key/value搜索场景

每一个关键码key,都有与它对应的值value,value可以是任何类型对象。树的结构中(每个节点)除了需要存储key,还需要存储value,增/删/查还是以key为关键字走二叉树的规则进行比较,可以快速找到与key对应的value。并且key/value的搜索场景支持了二叉树的修改,但是仍然不可以修改key,只可以修改value。

key/value二叉搜索树代码实现

	template<class K, class V>struct BSTNode{K _key;V _value;BSTNode<K, V>* _left;BSTNode<K, V>* _right;BSTNode(const K& key, const K& value):_key(key), _value(value), _left(nullptr), _right(nullptr){}};//搜索二叉树:template<class K, class V>class BSTree{typedef BSTNode<K, V> Node;public:BSTree() = default;BSTree(const BSTree<K, V>& m){_root = Copy(m._root);}BSTree<K, V>& operator=(BSTree<K, V> m){swap(_root, m._root);return *this;}~BSTree(){Destroy(_root);_root = nullptr;}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->_left;}else if (cur->_key < key){cur = cur->_right;}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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{if (nullptr == cur->_left){if (nullptr == parent){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;}else if (nullptr == cur->_right){if (nullptr == parent){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}else{Node* rightMinP = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinP->_left == rightMin){rightMinP->_left = rightMin->_right;}else{rightMinP->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (nullptr == root){return;}_InOrder(root->_left);cout << root->_key << ':' << root->_value << endl;_InOrder(root->right);}void Destroy(Node* root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}Node* Copy(Node* root){if (nullptr == root){return nullptr;}Node* newRoot = new Node(root->_key, root->_value);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}private:Node* _root = nullptr;};

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

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

相关文章

嘉立创编辑器中删除自己画的封装

快速创建一个元件及封装可参考 需要删除封装的原因 在添加新的元件时&#xff0c;有时候明明关联了封装和符号&#xff0c;但在原理图中添加元件时会出现封装未添加的问题。可能是这个立创EDA中有些功能问题很少使用&#xff0c;所以没完善。而且发现在封装中可以关联器件&am…

【开源鸿蒙】OpenHarmony 5.0.0 发布了,速来下载最新代码

【开源鸿蒙】OpenHarmony 5.0.0 发布了&#xff0c;速来下载最新代码 一、写在前面二、准备命令工具三、配置用户信息四、下载OpenHarmony源码4.1 使用ssh协议下载&#xff08;推荐&#xff09;4.2 使用https协议下载 五、下载编译工具链六、参考链接 今天是9月30号&#xff0c…

ThreadLocal原理解析及面试

基本使用 讲原理之前&#xff0c;我简单写个demo小程序 public class TestThreadLocal {public static void main(String[] args) throws InterruptedException {ThreadLocal<String> tl new ThreadLocal();/**主线程设置了一个值*/tl.set("SSSSSs");//tl.…

阿里云域名注册购买和备案

文章目录 1、阿里云首页搜索 域名注册2、点击 控制台3、域名控制台 1、阿里云首页搜索 域名注册 2、点击 控制台 3、域名控制台

linux如何与网络时间对齐(雪花算法ID重复)

文章目录 前言一、可能引发什么问题&#xff1f;二、调整步骤1.查看当前系统时间2.修改为中国时区3.同步网络时间4. 雪花id重复 总结 前言 linux服务器是部署服务的不二之选,有个小问题不可忽略&#xff1a; 会发现默认的服务器时间并非中国时区,时间也是相差八小时,中国时区…

8640 希尔(shell)排序

### 思路 希尔排序是一种基于插入排序的排序算法&#xff0c;通过将待排序数组分割成多个子序列分别进行插入排序来提高效率。初始增量d为n/2&#xff0c;之后每次减半&#xff0c;直到d为1。 ### 伪代码 1. 读取输入的待排序关键字个数n。 2. 读取n个待排序关键字并存储在数组…

Git傻傻分不清楚(上)

环境&#xff1a;Idea2022.3.3、Git&#xff08;忘辽~&#xff09; 怎么上传自己的项目到Github上&#xff1f; Idea和Github进行账号关联将项目上传到本地仓库&#xff08;Commit&#xff09;将本地仓库中的项目上传到Github上&#xff08;Push&#xff09; 一、关联账号 …

【Java SE 题库】移除元素(暴力解法)--力扣

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 目录 1. 题目 2. 解法(快慢“指针”) 3. 源码 4. 小结 1. 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要原地移除所有数值等于 val 的元素。元素的顺…

YOLOv10改进,YOLOv10改进主干网络为GhostNetV2(华为的轻量化架构)

摘要 摘要:轻量级卷积神经网络(CNN)专为移动设备上的应用而设计,具有更快的推理速度。卷积操作只能在窗口区域内捕捉局部信息,这限制了性能的进一步提升。将自注意力引入卷积可以很好地捕捉全局信息,但会极大地拖累实际速度。本文提出了一种硬件友好的注意力机制(称为 D…

PHP安装后Apache无法运行的问题

问题 按照网上教程php安装点击跳转教程&#xff0c;然后修改Apache的httpd.conf文件&#xff0c;本来可以运行的Apache&#xff0c;无法运行了 然后在"C:\httpd-2.4.62-240904-win64-VS17\Apache24\logs\error.log"&#xff08;就是我下载Apache的目录下的logs中&am…

多线程——认识线程(Thread)

目录 前言 一、第一个多线程程序 1.程序编写 2.介绍jconsole 二、创建线程 1.继承Thread类 ①重写run方法 ②重写run方法&#xff0c;使用匿名内部类 2.实现Runnable接口 ①重写run方法 ②重写run方法&#xff0c;使用匿名内部类 ③使用 lambda 表达式 三、多线程…

解决MySQL报Incorrect datetime value错误

目录 一、前言二、问题分析三、解决方法 一、前言 欢迎大家来到权权的博客~欢迎大家对我的博客进行指导&#xff0c;有什么不对的地方&#xff0c;我会及时改进哦~ 博客主页链接点这里–>&#xff1a;权权的博客主页链接 二、问题分析 这个错误通常出现在尝试将一个不…

OpenCV学堂 | YOLOv8官方团队宣布YOLOv11 发布了

本文来源公众号“OpenCV学堂”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;YOLOv8官方团队宣布YOLOv11 发布了 引言 YOLO11是Ultralytics YOLO系列实时目标检测器的最新迭代版本&#xff0c;它以尖端的准确性、速度和效率重新…

Mybatis-Plus新花样(一)

一. ActiveRecord Active Record(活动记录)&#xff0c;是一种领域模型模式&#xff0c;特点是一个模型类对应关系型数据库中的一个表&#xff0c;而模型类的一个实例对应表中的一行记录。 在MyBatisPlus中&#xff0c;AR模式即在实体类中封装了对数据库的访问&#xff0c;而不…

Qt_绘图

目录 1、绘图核心类 2、QPainter类的使用 2.1 绘制线段 2.2 绘制矩形 2.3 绘制圆形 2.4 绘制文本 3、QPen类的使用 3.1 使用画笔 4、QBrush类的使用 4.1 使用画刷 5、绘制图片 5.1 测试QPixmap 5.1.1 图片移动 5.1.2 图标缩小 5.1.3 旋转图片 5.1.4 将…

比较10大热门低代码开发平台及其适用性

本文介绍10款主流低代码开发平台&#xff0c;包括ZohoCreator、OutSystems、Mendix等&#xff0c;它们各具特色&#xff0c;如定制能力强、集成方便、全栈开发等&#xff0c;适合不同企业快速构建应用程序&#xff0c;提升开发效率。 一、Zoho Creator Zoho Creator低代码开发…

回溯大总结

目录 0、基础什么是回溯&#xff1f;回溯法解决的问题回溯模板 1、组合问题77. 组合216.组合总和III17. 电话号码的字母组合39. 组合总和&#xff1a;40.组合总和II 0、基础 什么是回溯&#xff1f; 回溯是一种穷举的搜索算法&#xff0c;并不是一个高效的算法&#xff0c;当…

9.数据结构与算法-单链表,循环链表和双向链表的比较////顺序表和链表的比较

单链表&#xff0c;循环链表和双向链表的时间效率比较 顺序表和链表的区别 存储密度

坡印廷矢量(也叫功率流密度,对面积积分就是功率)

坡印廷矢量在静电场&#xff0c;静磁场&#xff0c;恒定电流的电场&#xff0c;和时变电磁场中的表达式不同。 我们看时变电磁场的坡印廷矢量 坡印廷矢量就等于这个&#xff0c;其中的电场和磁场是实数表示的 坡印廷矢量用复数形式的场求 这里的E和H是复数表示的场&#xff0…

Qt界面优化——QSS

文章目录 QSS基本语法使用示例样式和代码分离选择器用法子控件选择器伪类选择器盒子模型控件样式示例按钮复选框输入框列表框菜单 登录界面 QSS基本语法 Qt对界面进行优化&#xff0c;主要采用的是QSS。 选择器 {属性名: 属性值; }选择器&#xff1a;先选择某个/类控件&#…