c++二叉树的进阶--二叉搜索树

1. 二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

注意:二叉树的中序遍历是一个有序(升序)序列。 

2. 二叉搜索树的操作 

2.1  二叉搜索树的查找 

假设要查找的数为x 。

1. 从根节点开始比较,x大于根去右树找,小于根去左数找。

2. 最多查找高度次,如果走到空就代表没有找到,返回false。

2.2 二叉搜索树的插入

假设插入的数为x

1. 如果树为空,直接新增节点,将x赋值给root。

2.  如果树不为空,则查找x应该插入的位置,然后插入新节点。

2.3 二叉搜索树的删除 

 要删除某一结点,我们需要考虑该节点是否有孩子,并在其子树中择一节点放入该节点的位置。

因此删除我们不仅要知道该节点,还要知道该节点的孩子,同时还要知道该节点的父亲,才能向该节点插入其孩子。但是,如果我们使用递归,传入该节点的引用,我们就可以直接对该节点进行操作而不需要知道其父节点。

删除分为以下几种情况:设要删除的节点为cur,其父亲为parent <因为树是单向的,要链接节点必须知道上一层的位置>

1. cur没有孩子,那么直接删除即可

2. cur左右子树有一个为空,这时候需要将他不为空的一侧孩子挪到cur处,由于我们并不知道cur在parent的哪一侧,因此需要判断cur在parent的哪一侧,然后进行删除与覆盖。

3. cur左右子树都不为空,这时候我们会选择将左树的最右节点or右树的最左节点替换到cur处,以右树的最左节点为例,此时只需要考虑替换后的cur是否有右树< 注意,这里右树的最左节点有可能是cur的右孩子,如果该节点有右树,将其右孩子给他即可>。回到第二步。

3.二叉搜索树的实现

3.1 Find

Node* Find(const K& key)//查找结点,返回其位置
{Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return cur;}}return nullptr;
}

3.2 Insert 

bool Insert(const K& key, const V& value)//插入
{if (_root == nullptr)//如果空树,直接构建根节点{_root = new Node(key, value);return true;}Node* cur = _root;Node* parent = nullptr;//由于树是单向的,因此我们需要知道插入结点的父亲才能进行链接while (cur)//寻找要插入的节点位置:不为空说明已有{if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (key < parent->_key)//确定要插入节点在父亲的左还是右{parent->_left = cur;}else{parent->_right = cur;}return true;
}

3.3 Erase 

bool Erase(const K& key)//删除
{Node* cur = _root, * parent = nullptr;//同样需要记录被删除的节点父亲,要用别的节点替换,与树进行链接while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr)//cur只有一个节点或没有节点{if (cur == _root)//cur可能在根节点处{_root = cur->_right;}if (cur == parent->_right){parent->_right = cur->_right;}else if (cur == parent->_left){parent->_left = cur->_right;}delete cur;return true;}else if (cur->_right == nullptr)//cur只有一个节点或没有节点{if (cur == _root){_root = cur->_left;}if (cur == parent->_right){parent->_right = cur->_left;}else if (cur == parent->_left){parent->_left = cur->_left;}delete cur;return true;}else//cur有两个节点,需要替换法,找右树的最左节点{Node* rightminParent = cur;Node* rightmin = cur->_right;while (rightmin->_left){rightminParent = rightmin;rightmin = rightmin->_left;}cur->_key = rightmin->_key;cur->_value = rightmin->_value;if (rightminParent->_right = rightmin)//右树如果没有左树,那么最左节点就是cur->rightrightminParent->_right = rightmin->_right;elserightminParent->_left = rightmin->_right;delete rightmin;return true;}}}return false;
}

3.4 Inorder 

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

3.5 测试代码 

	void TestBSTree(){BSTree<string, string> dict;dict.Insert("insert", "插入");dict.Insert("erase", "删除");dict.Insert("left", "左边");dict.Insert("string", "字符串");dict.InOrder();string str = "insert";dict.Erase(str);dict.InOrder();string str1 = "insert";BSTreeNode<string,string>* ret = dict.Find(str1);if (ret == nullptr){cout << str1<<"没有找到!" << endl;}elsecout << str1 << " : " << ret->_value << endl;cout << endl;string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };// 统计水果出现的次BSTree<string, int> countTree;for (auto str : strs){auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();}

3.5 运行结果

当然,这些接口都可以用递归实现,不过只有Erase有用递归实现的价值,在Erase中传入节点的别名,可以避免记录节点的父亲。 

4. 二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
式在现实生活中非常常见:
比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对。我们上面模拟实现的二叉搜索树就是KV模型。

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

从上面的实现中我们可以看出,无论是删除还是插入,我们都需要先进行查找,因此查找在一定程度上就表明了二叉搜索树的性能。

那么二叉搜索树的性能怎么样呢?

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

对于上面的二叉搜索树,其查找效率是截然不同的。

因此我们可以得出结论:

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

 

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

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

相关文章

Swift-27-类的初始化与销毁

Swift的初始化是一个有大量规则的固定过程。初始化是设置类型实例的操作&#xff0c;包括给每个存储属性初始值&#xff0c;以及一些其他准备工作。完成这个过程后&#xff0c;实例就可以使用了。 简单来讲就是类的构造函数&#xff0c;基本语法如下&#xff1a; 注意&#xff…

C语言扫雷游戏完整实现(上)

文章目录 前言一、新建好头文件和源文件二、实现游戏菜单选择功能三、定义游戏函数四、初始化棋盘五、 打印棋盘函数六、布置雷函数七、玩家排雷菜单八、标记功能的菜单九、标记功能菜单的实现总结 前言 C语言从新建文件到游戏菜单&#xff0c;游戏函数&#xff0c;初始化棋盘…

JavaScript-4.正则表达式、BOM

正则表达式 正则表达式包含在"/"&#xff0c;"/"中 开始与结束 ^ 字符串的开始 $ 字符串的结束 例&#xff1a; "^The"&#xff1a;表示所有以"The"开始的字符串&#xff08;"There"、"The cat"等&#x…

数据结构(八)——排序

八、排序 8.1 排序的基本概念 排序(Sort)&#xff0c;就是重新排列表中的元素&#xff0c;使表少的元素满足按关键字有序的过程。 输入∶n个记录R1,R2...., Rn&#xff0c;对应的关键字为k1, k2,... , kn 输出:输入序列的一个重排R1,R2....,Rn&#xff0c;使得有k1≤k2≤...≤…

综合大实验

题目&#xff1a; 1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两个环回&#xff0c;其…

VUE父组件向子组件传递值

创作灵感 最近在写一个项目时&#xff0c;遇到了这样的一个需求。我封装了一个组件&#xff0c;这个组件需要被以下两个地方使用&#xff0c;一个是搜索用户时用到&#xff0c;一个是修改用户信息时需要用到。其中&#xff0c;在搜索用户时&#xff0c;可以根据姓名或者账号进…

[前端]NVM管理器安装、nodejs、npm、yarn配置

NVM管理器安装、nodejs、npm、yarn配置 NVM管理器安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 nvm下载地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases 1.全部…

Unity面向切面编程

一直说面向AOP&#xff08;切面&#xff09;编程&#xff0c;好久直接专门扒出理论、代码学习过。最近因为某些原因&#x1f62d;还得再学学造火箭的技术。 废话不多说&#xff0c;啥是AOP呢&#xff1f;这里我就不班门弄斧了&#xff0c;网上资料一大堆&#xff0c;解释的肯定…

mybatis中<if>条件判断带数字的字符串失效问题

文章目录 一、项目背景二、真实错误原因说明三、解决方案3.1针对纯数字的字符串值场景3.2针对单个字符的字符串值场景 四、参考文献 一、项目背景 MySQL数据库使用Mybatis查询拼接select语句中进行<if>条件拼接的时候&#xff0c;发现带数字的或者带单个字母的字符串失效…

CPU资源控制

一、CPU资源控制定义 cgroups&#xff08;control groups&#xff09;是一个非常强大的linux内核工具&#xff0c;他不仅可以限制被namespace隔离起来的资源&#xff0c; 还可以为资源设置权重、计算使用量、操控进程启停等等。 所以cgroups&#xff08;control groups&#xf…

Netty学习——实战篇5 Netty 心跳监测/WebSocket长连接编程 备份

1 心跳监测 MyServer.java public class MyServer {public static void main(String[] args) {NioEventLoopGroup bossGroup new NioEventLoopGroup(1);NioEventLoopGroup workerGroup new NioEventLoopGroup();try {ServerBootstrap serverBootstrap new ServerBootstrap…

学习Docker笔记

在23号刚刚学完新版本的docker还想说回去继续学习老版本的springcloud课程里面的docker 结果一看黑马首页新版本课程出了&#xff0c;绷不住了。以下是我学习新版本docker的笔记&#xff0c;记录了我学习过程遇到的各种bug和解决&#xff0c;也参考了黑马老师的笔记&#xff1a…

TDengine高可用探讨

提到数据库&#xff0c;不可避免的要考虑高可用HA&#xff08;High Availability&#xff09;。但是很多人对高可用的理解并不是很透彻。 要搞清高可用需要回答以下几个问题&#xff1a; 什么是高可用&#xff1f;为什么需要高可用&#xff1f;高可用需要达到什么样的目标&am…

Unity射线实现碰撞检测(不需要rigbody组件)

使用physic.CapsulCast&#xff08;&#xff09;&#xff1b; 前面3个参数生成一个胶囊体&#xff0c; 向着发射方向&#xff0c;发射出一串的胶囊&#xff08;没有最大距离&#xff09; 有最大距离&#xff0c;可以节约性能开销。 physic.CapsulCast&#xff08;&#xff0…

easypoi 导出增加自增序列

要求&#xff1a;使用easypoi导出Excel的时候&#xff0c;要求增加”序号“列&#xff0c;从1开始增加。这列与业务数据无关&#xff0c;只是展示用&#xff0c;便于定位。如下图所示 实现方式&#xff1a;Java对象新增一列&#xff0c;注意name "序号", format &…

Linux-缓冲区(简单理解)

1. 缓冲区是什么 缓冲区就是一段内存空间。 2. 为什么要有缓冲区 IO写入有两种&#xff1a; 写透模式&#xff08;WT&#xff09; 成本高&#xff0c;效率低写回模式&#xff08;WB&#xff09; 成本低&#xff0c;效率高 写透模式&#xff1a;每次的文件写入都要立即刷新…

使用ClickHouse和Terraform进行CI/CD

本文字数&#xff1a;11047&#xff1b;估计阅读时间&#xff1a;28 分钟 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 简介 在 ClickHouse&#xff0c;我们致力于以 API 为先的开发方式来构建 ClickHouse Cloud。用户通过用户界面…

如何快速学习盲打键盘的指法

学习盲打键盘的指法需要一定的时间和练习&#xff0c;但是以下几个方法可以帮助你加快学习的速度&#xff1a; 掌握正确的手位&#xff1a;了解标准的键盘布局以及手指应该放置的位置是学习盲打的第一步。在QWERTY键盘上&#xff0c;你的左手应该放在ASDF键上&#xff0c;右手应…

人工智能入门(一):基于Pytorch的手写数字识别模型

前言&#xff1a; 因为还在上学&#xff0c;时间不太够用&#xff0c;很多内容写到后面心有余力不足&#xff0c;未来有时间我会慢慢补充。人工智能的知识涉猎范围广又杂乱无章&#xff0c;啃书或上课学到的知识往往很早就过时了或者离实际的项目无关。所以&#xff0c;我很希…

VS调试、debug和release、栈区底层简单介绍、const 修饰指针变量介绍

文章目录 前言一、调试二、debug和release三、调试需要多用&#xff0c;多熟悉四、栈区底层简单介绍五、优秀的代码&#xff1a;常见的coding技巧: 六、const 修饰指针变量1. const 出现在 * 左边2. const 出现在 * 右边 七、strcpy函数的仿写1.版本12. 版本23. 版本34. 版本4 …