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

文章目录

  • 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;发上来看看有没有适合朋友们的需求。 今天这个数据提供了非常全的词汇单词以及词汇对应的含义以及近…

将Java项目Jar包制作成Docker镜像

文章目录 前言一、准备事项二、使用步骤1.Dockerfile脚本2.制作镜像推送Harbor仓库前言 以前单体项目通常采用传统部署方式将项目打成Jar包再进行部署。如果我们项目是微服务则需要进行Docker容器部署。本文将介绍如何在本地将Jar包制作成Docker镜像并推送到Harbor仓库 一、准…

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;接触最多的应该是通过…

验证与分享执行计划突变引发的问题

作者简介 张瑞远&#xff0c;曾经从事银行、证券数仓设计、开发、优化类工作&#xff0c;现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作。 持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得的专业技能与认证包括 Oce…

被军训到的两天

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

如何更改SonarQube的JDK版本

如何更改SonarQube的JDK版本 当需要升级或更换SonarQube所使用的JDK版本时&#xff0c;可以按照以下步骤进行操作&#xff1a; 第一步&#xff1a;确定新JDK的安装路径 首先&#xff0c;您需要找到您打算使用的JDK的安装路径。这通常是一个包含JDK各种工具和库的文件夹。请确…

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

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

性能测试-Redis

一、测试注意点 1、缓存预热 如果程序初次运行&#xff0c;此时由于数据尚未加载到缓存&#xff0c;则程序的响应时间会明显变长 注意事项&#xff1a; 性能测试的时候 出现 非常不稳定的现象程序刚启动&#xff0c;它的性能 明显 低于 已经运行一段时间的 1.1 测试缓存没…

基于SQL语句的数据脱敏实现方法

文章目录 要解决什么背景Jsqlparser安全系统难点目标 方法语法树遍历实现初始化第一轮第二轮第三轮 总结 要解决什么 企业进行软件开发、数据分析、数据查询时&#xff0c;不可避免的涉及到数据安全的问题&#xff0c;不同人员能看到的数据不同&#xff0c;如何识别哪些敏感数…

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; 希望…

Hashtable、HashMap、TreeMap的区别

了解 Hashtable、HashMap、TreeMap 1&#xff09;Hashtable 是一个比较古老的实现&#xff0c;随 Java 1.0 引入。 它是同步的&#xff0c;这意味着它是线程安全的&#xff0c;但这也意味着它通常比非同步的实现&#xff08;如 HashMap &#xff09;慢。 不允许使用 null 键…

x264编码器 API 函数介绍

x264 x264是一个开源的视频编码库,用于将视频压缩为H.264/AVC(Advanced Video Coding)格式。它是一种广泛使用的视频编码标准,能够提供高质量的视频压缩和较低的比特率。 x264库提供了一个编码器,可以将原始视频序列转换为H.264/AVC压缩的比特流。它实现了各种H.264编码算…

【从Python基础到深度学习】12. 封包、解包

封包 将多个值赋值给一个变量时&#xff0c;Python 会自动将这些值封装成元组&#xff0c;这个特性称之为封包 a 1, 2, 3, 4 print(a) 解包 所有可迭代对象都支持解包 str list tuple dict set reversed range zip enumerate 赋值过程中的解包赋值符号左边变量和右边可迭代…

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

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