【数据结构】二叉搜索树底层刨析

文章目录

  • 1. 二叉搜索树的实现
  • 2. 二叉搜索树的应用
  • 3. 改造二叉搜索树为 KV 结构
  • 4. 二叉搜索树的性能分析

在这里插入图片描述

1. 二叉搜索树的实现

namespace key
{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() = default;BSTree(const BSTree<K>& t){_root = Copy(t._root);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}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;}else{rightMinParent->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}private:void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}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 _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}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 _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 _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{Node* del = root;if (root->_right == nullptr){root = root->_left;}else if (root->_left == nullptr){root = root->_right;}else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);return _EraseR(root->_right, key);}delete del;return true;}}private:Node* _root = nullptr;};
}

2. 二叉搜索树的应用

  1. K 模型:K 模型即只有 key 作为关键码,结构中只需要存储 key 即可,关键码即为需要搜索到的值

    比如:给一个单词 word,判断该单词是否拼写正确,具体方式如下:

    • 以词库中所有单词集合中的每个单词作为 key,构建一颗二叉搜索树;
    • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV 模型:每一个关键码 key,都有与之对应的值 value,即 <Key, Value> 的键值对。这种方式在现实生活中非常常见:

    • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文 <word, chinese> 就构成一种键值对;
    • 再比如统计单词个数,统计成功后,给定单词就可以快速找到其出现的次数,单词与其出现次数 <word, count> 就构成一种键值对

3. 改造二叉搜索树为 KV 结构

namespace key_value
{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;}else{rightMinParent->_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 << " ";cout << root->_value << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};
}

测试代码:

namespace key_value
{void TestBSTree1(){// 输入单词,查找单词对应的中文翻译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 TestBSTree2(){// 统计水果出现的次数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();}
}

4. 二叉搜索树的性能分析

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

对有 n 和节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

问题:如果退化成单支树,二叉搜索树的性能就失去了。能否进行改进,不论按照什么次序插入关键码,都能使二叉搜索树的性能达到最优?

答:可以使用 AVL树红黑树 的特性,使单支树在达到一定深度时进行“旋转”,变回接近完全二叉树的形状,这个我们后面再谈。


END

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

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

相关文章

工作中用到的 —— 工作总结提炼出来的股文

这里是目录 ---------------- VUE相关 -----------------1 - Vue3 是怎么得更快的&#xff1f;1-1 Fragment [frɡˈment]1-2 Suspense [səˈspens]1-3 Teleport [ˈtelipɔːt]1-4 v-memo 2- 说一下 Composition API3- 说一下 setup4- watch 和 watchEffect 的区别5- Vue3 响…

Sublime查看ANSI编码文档乱码问题

原因为没有安装对应的解码插件。 选择安装插件包 选择插件包&#xff1a;ConvertToUTF8或者GBK&#xff0c;我试了第一个插件包不行&#xff0c;安装GBK插件包后OK。

Git如何清除账户凭证

场景&#xff1a;一般发生在Git用户变更的情况 1.git base 操作 Git会使用凭证助手 credential.helper来储存账户凭证&#xff0c;通过以下命令移除&#xff1a; git config --system --unset credential.helper 除了system系统级外&#xff0c;还有 global、local范围。 查…

20万英文单词同义词宝典ACCESS\EXCEL数据库

英语同义词反义词的数据之前搞到过《近万英语单词同义词典ACCESS数据库》、《上百万英语同义反义词词典ACCESS数据库》&#xff0c;今天又搞到一份几十万行数据的&#xff0c;发上来看看有没有适合朋友们的需求。 今天这个数据提供了非常全的词汇单词以及词汇对应的含义以及近…

Spring揭秘:ClassPathScanningProvider接口应用场景及实现原理!

技术应用场景 ClassPathScanningCandidateComponentProvider是Spring框架中一个非常核心的类&#xff0c;它主要用于在类路径下扫描并发现带有特定注解的组件&#xff0c;支持诸如ComponentScan、Component、Service、Repository和Controller等注解的自动扫描和注册。 ClassP…

Mysql 无法启动,mysql-bin.日志丢失删除处理

在linux操作系统中&#xff0c;当mysql无法启动时候&#xff0c;先看日志 2024-03-15T05:20:16.352075Z 0 [Warning] [MY-000081] [Server] option max_allowed_packet: unsigned value 107374182400 adjusted to 1073741824. 2024-03-15T05:20:16.352156Z 0 [Warning] [MY-010…

Marshmallow,一个有点甜的Python库

前言 在许多场景中&#xff0c;我们常常需要执行Python对象的序列化、反序列化操作。例如&#xff0c;在开发REST API时&#xff0c;或者在进行一些面向对象化的数据加载和保存时&#xff0c;这一功能经常派上用场。 经常cv Python代码的臭宝&#xff0c;接触最多的应该是通过…

被军训到的两天

1.gradle7.6.1 1.安装gradle7.6.1,一定要注意的是&#xff0c;使用的JDK是否能用&#xff0c;比如gradle7.6.1用的是JDK11。 2. F:/sofer....是Gradle自己的仓库地址&#xff0c;注意不能和maven使用一样的仓库。 使用specified location,可以避免下本项目的gradle版本&…

ego - 人工智能原生 3D 模拟引擎——基于AI的3D引擎,可以做游戏、空间计算、元宇宙等项目

1. 产品概述:Ego是一款AI本地化的3D模拟引擎,旨在让非技术创作者通过自然语言生成逼真的角色、3D世界和交互式脚本。该平台提供了创建和分享游戏、虚拟世界和交互体验的功能。 2. 定位:Ego定位于解决开放世界游戏和模拟的三大难题:难以编写游戏脚本、非玩家角色无法展现人…

linux系统关闭防火墙和SELINUX及配置网络

一&#xff0c;关闭防火墙和SELINUX 当我们进入界面后&#xff0c;输入用户名root&#xff0c;以及密码&#xff0c;密码我们是看不见的 然后输入指令cat -n /etc/sysconfig/selinux &#xff08;注意空格&#xff09; 输入指令 vi /etc/sysconfig/selinux &#xf…

【Python】进阶学习:一文了解NotImplementedError的作用

【Python】进阶学习&#xff1a;一文了解NotImplementedError的作用 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望…

MongoDB实战面试指南:常见问题一网打尽

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! MongoDB是一款流行的非关系型数据库&#xff0c;以其高效、可扩展的特性受到开发者的青睐。了解MongoDB的架构、存储引擎和数据结…

ETH共识升级之路

简介 根据我们之前的介绍&#xff0c;了解到ETH网络的共识方式&#xff0c;已经从 PoW 切换到了 PoS&#xff0c;今天我们就回顾下升级之路&#xff0c;以及升级带来的影响 最早的共识机制 PoW 以太坊创建之初采用了类似比特币的工作量证明机制&#xff0c;即矿工通过计算哈希函…

CircuitBreaker熔断器

CircuitBreaker熔断器 1、Hystrix目前也进入维护模式 ​ Hystrix是一个用于处理分布式系统的延迟和容错的开源库&#xff0c;在分布式系统里&#xff0c;许多依赖不可避免的会调用失败&#xff0c;比如超时、异常等&#xff0c;Hystrix能够保证在一个依赖出问题的情况下&…

C++演变历史

C 从 C 98 版本到今年确定的 C23&#xff0c;共经历了 6 个版本的迭代。上面这张图里&#xff0c;列出了每次版本更新变化的内容。顶部黑体字的大小说明了版本变化的大小&#xff0c;其中 C14 是字体最小&#xff0c;这个版本是 C11 的增量版本&#xff0c;之所以没有大的变动&…

Server-Sent Events (SSE) 实现从服务器到客户端的实时数据流

前期回顾 避免阻塞主线程 —— Web Worker 示例项目-CSDN博客https://blog.csdn.net/m0_57904695/article/details/136721297?spm1001.2014.3001.5501 目录 CSDN 彩色之外 &#x1f4dd; 前言 &#x1f6e0;️ 安装 ✂️ 运行服务器 ✂️ 运行index.html ♻️ 贡献…

引领短剧风尚,打造全新观影体验——短剧APP开发之旅

随着移动互联网的迅猛发展&#xff0c;短视频和短剧成为了大众休闲娱乐的新宠。为了满足用户对于高质量、快节奏内容的需求&#xff0c;我们决定开发一款全新的短剧APP&#xff0c;为用户带来前所未有的观影体验。 这款短剧APP将集合丰富多样的短剧资源&#xff0c;涵盖各种题…

了解什么是 Redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?

目录 一、面试官心理分析 二、面试题剖析 1. 缓存雪崩 2. 缓存穿透 3. 缓存击穿 一、面试官心理分析 其实这是问到缓存必问的&#xff0c;因为缓存雪崩和穿透&#xff0c;是缓存最大的两个问题&#xff0c;要么不出现&#xff0c;一旦出现就是致命性的问题&#x…

B/S基于云计算的云HIS智慧医院管理系统源码带电子病历编辑器

目录 一、系统概述 二、开发环境 三、系统功能 1、门诊部分 2、住院部分 3、电子病历 4、药物管理 5、统计报表 6、综合维护 7、运营运维 云HIS系统&#xff1a;病案首页 云his系统源码 SaaS应用 功能易扩 统一对外接口管理 现如今&#xff0c;大数据、云计算、移动…

【WPF】Canvas的Path画线 颜色值偏差和面积不准确

图像中左上角2个红色线段 颜色值有偏差&#xff0c;且线段高度不准确&#xff08;我设置的是Red,1&#xff09;。 解决方案&#xff1a; 1、PathGeometry PathFigure LineSegment 2、Stretch设置Fill <Path Stretch"Fill" Stroke"Red"…