C++进阶(五)二叉搜索树

在这里插入图片描述


📘北尘_:个人主页

🌎个人专栏:《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》

☀️走在路上,不忘来时的初心

文章目录

  • 一、二叉搜索树概念
  • 二、二叉搜索树操作
  • 三、二叉搜索树的实现
  • 四、二叉搜索树的应用
  • 五、二叉搜索树的性能分析


一、二叉搜索树概念

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

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

在这里插入图片描述


二、二叉搜索树操作

在这里插入图片描述

在这里插入图片描述

  1. 二叉搜索树的查找
    a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
    b、最多查找高度次,走到到空,还没找到,这个值不存在。
  2. 二叉搜索树的插入
    插入的具体过程如下:
    a. 树为空,则直接新增节点,赋值给root指针
    b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

在这里插入图片描述
3. 二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除。

在这里插入图片描述


三、二叉搜索树的实现

#include<iostream>
using namespace std;template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};
template<class K>
class BSTree
{typedef BSTreeNode<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){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){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->_left){parent->_left = cur->_right;}else {parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = cur -> _left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{Node* parent = cur;Node* subleft = cur->_right;while (subleft->_left){parent = subleft;subleft = subleft->_left;}swap(cur->_key, subleft->_key);if (subleft == parent->_left){parent->_left = subleft->_right;}else{parent->_right = subleft->_right;}delete subleft;}return true;}}return false;}~BSTree(){Destory(_root);}bool EraseR(const K& key){return _EraseR(_root, key);}bool InsertR(const K& key){return _InsertR(_root,key);}bool findR(const K& key){return _findR(_root, key);}void InOrder(){_InOrder(_root);cout << endl;}BSTree(const BSTree<K>& t){_root = Copy(t._root);}//C++11BSTree(){}BSTree<K>& operator=(BSTree<K> t){swap(_root,t._root);return *this;}private:Node* _root = nullptr;Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_key);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}void Destory(Node*& root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}bool _InsertR(Node*& root,const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return  _InsertR(root->_right,key);}else if (root->_key > key){return  _InsertR(root->_left,key);}else{return false;}}bool _findR(Node* root, const K* key){if (root == nullptr)return false;if (root->_key < key){return _findR(root->_right, key);}else if (root->_key > key){return _findR(root->_left, key);}else{return true;}}bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else{if (root->_left == nullptr){Node* del = root;root = root->_right;delete del;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete del;}else{Node* subleft = root->_right;while (subleft->_left){subleft = subleft->_left;}swap(subleft->_key, root->_key);return _EraseR(root->_right, key);}}return false;}};
int main()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> bt;for (auto e : a){bt.InsertR(e);}bt.InOrder();bt.EraseR(14);bt.InOrder();bt.EraseR(3);bt.InOrder();bt.EraseR(8);bt.InOrder();for (auto e : a){bt.EraseR(e);bt.InOrder();}return 0;
}

四、二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
    的值。
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
    比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
    再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
template<class K,class V>
struct BSTreeNode
{BSTreeNode<K,V>* _left;BSTreeNode<K,V>* _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){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){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->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{Node* parent = cur;Node* subleft = cur->_right;while (subleft->_left){parent = subleft;subleft = subleft->_left;}swap(cur->_key, subleft->_key);if (subleft == parent->_left){parent->_left = subleft->_right;}else{parent->_right = subleft->_right;}delete subleft;}return true;}}return false;}~BSTree(){Destory(_root);}void InOrder(){_InOrder(_root);}private:Node* _root = nullptr;void Destory(Node*& root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}};
void TestBSTree3()
{// 输入单词,查找单词对应的中文翻译BSTree<string, string> dict;dict.Insert("string", "字符串");dict.Insert("tree", "树");dict.Insert("left", "左边、剩余");dict.Insert("right", "右边");dict.Insert("sort", "排序");// 插入词库中所有单词string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret == nullptr){cout << "单词拼写错误,词库中没有这个单词:" << str << endl;}else{cout << str << "中文翻译:" << ret->_value << endl;}}
}
void TestBSTree4()
{// 统计水果出现的次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };BSTree<string, int> countTree;for (const auto& str : arr){// 先查找水果在不在搜索树中// 1、不在,说明水果第一次出现,则插入<水果, 1>// 2、在,则查找到的节点中水果对应的次数++//BSTreeNode<string, int>* ret = countTree.Find(str);auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();
}

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

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

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

在这里插入图片描述

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

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了。


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

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

相关文章

mac上搭建 hadoop 伪集群

1. hadoop介绍 Hadoop是Apache基金会开发的一个开源的分布式计算平台&#xff0c;主要用于处理和分析大数据。Hadoop的核心设计理念是将计算任务分布到多个节点上&#xff0c;以实现高度可扩展性和容错性。它主要由以下几个部分组成&#xff1a; HDFS (Hadoop Distributed Fi…

如何理解计算机中playload这个词?

playload的翻译是载荷&#xff0c;负载。放在计算机场景中总感觉有点抽象。 我最经常接触的playload是发送post请求时所携带的json参数&#xff0c;就是playload。 联系到playload更精确的翻译是&#xff1a;n. (飞机、船只的)有效载荷&#xff0c;有酬负载&#xff1b;(车辆等…

PyTorch各种损失函数解析:深度学习模型优化的关键(1)

目录 详解pytorch中各种Loss functions binary_cross_entropy 用途 用法 参数 数学理论 示例代码 binary_cross_entropy_with_logits 用途 用法 参数 数学理论 示例代码 poisson_nll_loss 用途 用法 参数 数学理论 示例代码 cosine_embedding_loss 用途 …

揭秘程序栈:你的代码在幕后是怎么运行的?

计算机科学中&#xff0c;许多概念和原理可能会让开发者感到头疼&#xff0c;比如程序栈。这个看似晦涩的概念&#xff0c;实对我们理解程序运行至关重要。本文将以通俗易懂的方式&#xff0c;带你深入理解程序栈的工作原理和优化策略。 一、为什么需要栈&#xff1f; 栈是一…

Diffusion Models

DDPM x 0 ∼ q ( x 0 ) x_0 \sim q(x_0) x0​∼q(x0​)是真实数据分布&#xff0c;扩散模型学习一个分布 p θ ( x 0 ) p_\theta(x_0) pθ​(x0​)去逼近真实数据分布。 p θ ( x 0 ) : ∫ p θ ( x 0 : T ) d x 1 : T (1) p_\theta(x_0) : \int p_\theta(x_{0:T})dx_{1:T} \…

chatgpt的实用技巧四temperature 格式

四、temperature 格式 GPT3.5 temperature 的范围为&#xff1a;0-0.7&#xff1b; GPT4.0 temperature 的范围为&#xff1a;0-1&#xff1b; 当 temperature 为 0 时候&#xff0c;结果可稳定。 当 temperature 为 0.7/1 时候&#xff0c;结果发散具备创力。 数值越大&a…

Flink的两阶段提交是什么

两阶段提交 Two-Phase-Commit&#xff0c;简称 2PC&#xff0c;是很常用的解决分布式事务问题的方式&#xff0c;它可以保证在分布式事务中&#xff0c;要么所有参与进程都提交事务&#xff0c;要么都取消&#xff0c;即实现 ACID 中的 A &#xff08;原子性&#xff09;。在数…

设计模式的学习笔记

设计模式的学习笔记 一. 设计模式相关内容介绍 1 设计模式概述 1.1 软件设计模式的产生背景 设计模式最初并不是出现在软件设计中&#xff0c;而是被用于建筑领域的设计中。 1977 年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任 Christopher Alexander 在…

【Redis数据类型】String实现及应用场景

文章目录 String1、介绍2、内部实现整数值embstr 编码字符串raw编码字符串 3、常用命令4、应用场景缓存对象常规计数分布式锁共享session信息 参考&#xff1a;小林Coding Redis九种数据类型 Redis 提供了丰富的数据类型&#xff0c;常见的有五种&#xff1a;String&#xff08…

Debian 10.13.0 安装图解

引导和开始安装 这里直接回车确认即可&#xff0c;选择图形化安装方式。 选择语言 这里要区分一下&#xff0c;当前选中的语言作为安装过程中安装器所使用的语言&#xff0c;这里我们选择中文简体。不过细心的同学可能发现&#xff0c;当你选择安装器语言之后&#xff0c;后续安…

C#: BitConverter 字节数组byte[ ] 转各种数据类型用法列举

说明&#xff1a;C# BitConverter 字节数组byte[ ] 转各种数据类型用法示例 1.ToBoolean(byte[] value, int startIndex)&#xff1a;将指定字节数组中从指定索引开始的两个字节转换为布尔值。 byte[] bytes { 1, 0 }; bool result BitConverter.ToBoolean(bytes, 0); // 输…

MySQL 8.0中删除的选项和变量

以下系统变量、状态变量和选项已在 MySQL 8.0 中删除&#xff1a; • Com_alter_db_upgrade: ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME 语句的计数。在 MySQL 8.0.0 版中删除。 • Innodb_available_undo_logs: InnoDB 回滚段的总数&#xff1b;与 innodb_rollback_se…

电力能源三维可视化合集 | 图扑数字孪生

电力能源是现代社会发展和运行的基石&#xff0c;渗透于工业、商业、农业、家庭生活等方方面面&#xff0c;它为经济、生活质量、环境保护和社会发展提供了巨大的机会和潜力。图扑软件应用自研 HT for Web 强大的渲染引擎&#xff0c;助力现代化的电力能源数字孪生场景&#xf…

运筹说 第95期 | 非线性规划奠基人——库恩与塔克

经过之前的学习&#xff0c;相信大家已经对运筹学的网络计划的内容有了一定的了解&#xff0c;接下来小编将带你学习新一章——非线性规划的内容&#xff0c;让我们先来了解一下非线性规划的诞生和发展历程&#xff0c;然后共同走近非线性规划领域的代表人物——库恩和塔克&…

2.控制语句

1.分支语句/判断语句 if 语句 if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } if…else 语句 if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 / } else { / 如果布尔表达式为假将执行的语句 */ } if…else if…else语句 if(boolean_expressi…

【BERT】详解

BERT 简介 BERT 是谷歌在 2018 年时提出的一种基于 Transformer 的双向编码器的表示学习模型&#xff0c;它在多个 NLP 任务上刷新了记录。它利用了大量的无标注文本进行预训练&#xff0c;预训练任务有掩码语言模型和下一句预测&#xff0c;掩码语言模型指的是随机地替换文本中…

linux下boost编译arm版本

1.下载 https://www.boost.org/users/history/version_1_74_0.html 2.解压 tar -zxvf boost_1_74_0.tar.gz 2.编译 ./bootstrap.sh --with-librariesfilesystem,thread --with-toolsetgcc3 替换gcc 修改配置文件project-config.jam,将下列的arm gcc编译器替换为你本机的编译器…

Java 进阶之旅的第四天

Java 进阶之旅的第四天 文章目录 Java 进阶之旅的第四天Collections集合工具类Collections集合工具类作用/特点Collections常用的API Collections集合工具类 Collections集合工具类作用/特点 可以对创建的集合进行操作,比如批量添加等查看源码可得,该工具类的构造函数用priva…

Python基础学习:同步异步阻塞与非阻塞

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 一、状态介绍 在了解其他概念之前&#xff0c;我们首先要了解进程的几个状态。 在程序运行的过程中&#xff0c;由于被操作系统的调度算法控制&#xff0c;程序…

k8s---pod控制器

pod控制器发的概念&#xff1a; 工作负载&#xff0c;workload用于管理pod的中间层&#xff0c;确保pod资源符合预期的状态。 预期状态&#xff1a; 1、副本数 2、容器重启策略 3、镜像拉取策略 pod出故障的出去等等 pod控制器的类型&#xff1a; 1、replicaset&#xf…