【C++数据结构】二叉搜索树

【C++数据结构】二叉搜索树

目录

  • 【C++数据结构】二叉搜索树
      • 二叉搜索树概念
      • 二叉搜索树操作
          • 二叉搜索树的查找
          • 二叉搜索树的插入
          • 二叉搜索树的删除
          • 二叉搜索树的实现
          • 二叉搜索树的应用
          • 二叉搜索树的性能分析

作者:爱写代码的刚子
时间:2023.8.22
前言:二叉搜索树是为后面的map和set做铺垫,在有些题目中难度较大,需要我们打好二叉树的基础。

二叉搜索树概念

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

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

二叉搜索树操作

二叉搜索树的查找

在这里插入图片描述

二叉搜索树的插入
  1. a树为空,则直接插入
  2. 树不空,按二叉搜索树性质查找插入位置,插入新节点
  • 如果插入的节点大于当前节点,就走右子树;如果插入的节点小于当前节点,就走左子树。
二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中, 再来处理该结点的删除问题

二叉搜索树的实现

以下是我模拟实现二叉搜索树的代码

template<class T>
class BSTreeNode
{
public:BSTreeNode<T>* _left;BSTreeNode<T>* _right;T _key;BSTreeNode(const T&key):_left(nullptr),_right(nullptr),_key(key){}
};template<class T>
class BSTree
{typedef BSTreeNode<T> Node;
public:BSTree():_root(nullptr){}BSTree(const BSTree<T>& tree){_root=_Copy(tree._root);}~BSTree(){_Destroy(_root);}BSTree<T>& operator=(BSTree<T> tree){swap(_root,tree._root);return *this;}bool InSert(const T&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->_left;}else if(cur->_key<key){parent=cur;cur=cur->_right;}else{return false;}}cur = new Node(key);if(parent->_key<key){parent->_right=cur;}else{parent->_left=cur;}return true;}bool Find(const T&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 T&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(cur->_left==nullptr){if(cur==_root){_root=cur->_right;}else if(parent->_right==cur)//判断在哪棵树,左树就连左树,右树就连右树{parent->_right=cur->_right;}else{parent->_left=cur->_right;}}else if(cur->_right==nullptr){if(cur==_root){_root=cur->_left;}else if(parent->_right==cur){parent->_right=cur->_left;}else{parent->_left=cur->_left;}}else{Node*parent=cur;//这里父节点不能置空,考虑cur为头节点的情况Node*leftMax=cur->_left;//下面的步骤是找左子树的最大节点while(leftMax->_right){parent=leftMax;leftMax=leftMax->_right;}swap(cur->_key,leftMax->_key);if(parent->_left==leftMax)//特殊情况{parent->_left=leftMax->_left;}else if(parent->_right==leftMax){parent->_right=leftMax->_left;}cur=leftMax;}delete cur;cur=nullptr;return true;}}return false;} bool FindR(const T&key)//递归实现{return _FindR(_root,key);}void InOrder(){_InOrder(_root);cout<<endl;}bool InSertR(const T&key){return _InSertR(_root,key);}bool EraseR(const T&key){return _EraseR(_root,key);}
private:Node* _Copy(const Node* root){if(root==nullptr){return nullptr;}Node* copynode=new Node(root->_key);copynode->_left=_Copy(root->left);copynode->_right=_Copy(root->_right);return copynode;}bool _FindR(const Node*root,const T&key){if(root==nullptr){return false;}if(root->_key>key){return _FindR(root->_left,key);}else if(root->_key<key){return _FindR(root->_right,key);}else{return true;}}void _Destroy(Node*&root){if(root==nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;root=nullptr;}void _InOrder(const Node*root){if(root==nullptr){return;}_InOrder(root->_left);cout<<root->_key<<" ";_InOrder(root->_right);}bool _InSertR(Node*&root,const T&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;}//return false;}bool _EraseR(Node*&root,const T&key){if(root==nullptr){return false;}if(root->_key>key){return _EraseR(root->_left,key);}else if(root->_key<key){return _EraseR(root->_right,key);}else{Node*save=root;if(root->_left==nullptr){root=root->_right;//使用引用的妙处,不需要判断左右子树}else if(root->_right==nullptr){root=root->_left;}else{Node*leftMax=root->_left;while(leftMax->_right){leftMax=leftMax->_right;}swap(root->_key,leftMax->_key);return _EraseR(root->_left,key);//注意这里不是传root}delete save;return true;}}
private:Node* _root;
};

几个需要注意的地方:

  • bool _EraseR(Node*&root,const T&key)这个函数中return _EraseR(root->_left,key);不能写为return _EraseR(root,key);否则会出现头节点删除不干净的情况:
    举例:
    在这里插入图片描述
  • bool Erase(const T&key)函数中
if(parent->_left==leftMax)//特殊情况
{parent->_left=leftMax->_left;
}

是为了解决如下情况:
在这里插入图片描述

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

(举例代码略过)

二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的 深度的函数,即结点越深,则比较次数越多。

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

在这里插入图片描述
在这里插入图片描述

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为: log2N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为: N/2


要学好搜索二叉树的应用还是要多刷题,多看看原理。

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

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

相关文章

无涯教程-PHP - preg_split()函数

preg_split() - 语法 array preg_split (string pattern, string string [, int limit [, int flags]]); preg_split()函数的操作与split()完全相同&#xff0c;只不过正则表达式被接受为pattern的输入参数。 如果指定了可选的输入参数limit&#xff0c;则仅返回子字符串的限…

WebGL 变量uniform、gl.getUniformLocation、gl.uniform4f及其同族函数相关

目录 uniform变量命名规范 获取 uniform 变量的存储地址 gl.getUniformLocation 向uniform变量赋值 gl.uniform4f ​编辑 gl.uniform4f()的同族函数 demo&#xff1a;点击webgl坐标系的四个象限绘制各自不同颜色的点 uniform变量命名规范 var FSHADER_SOURCE uniform vec4…

小程序-基于vant的Picker组件实现省市区选择

一、原因 因vant/area-data部分的市/区数据跟后台使用的高德/腾讯省市区有所出入&#xff0c;故须保持跟后台用同一份数据&#xff0c;所以考虑以下几个组件 1、Area 2、Cascader 3、Picker 因为使用的是高德地图的省市区json文件&#xff0c;用area的话修改结构代价太大&…

AVL——平衡搜索树

✅<1>主页&#xff1a;我的代码爱吃辣&#x1f4c3;<2>知识讲解&#xff1a;数据结构——AVL树☂️<3>开发环境&#xff1a;Visual Studio 2022&#x1f4ac;<4>前言&#xff1a;AVL树是对二叉搜索树的严格高度控制&#xff0c;所以AVL树的搜索效率很高…

xargs 的用法 在1个文件夹中批量删除文件,这些删除的文件名是另一个文件夹中的文件名。

xargs 的用法 在1个文件夹中批量删除文件&#xff0c;这些删除的文件名是另一个文件夹中的文件名。 1、问题背景 应用场景 1、问题背景 应用场景 在二进制部署docker时&#xff0c;会把docker的所有可执行文件复制到/usr/bin下。 如果说复制过去后&#xff0c;想要反悔&#x…

VB.NET通过VB6 ActiveX DLL调用PowerBasic及FreeBasic动态库

前面说的Delphi通过Activex DLL同时调用PowerBasic和FreeBasic写的DLL&#xff0c;是在WINDOWS基础平台上完成的。 而 .NET平台是架在WINDOWS基础平台之上的&#xff0c;它的上面VB.NET或C#等开发的APP程序&#xff0c;下面写一下用VB.NET&#xff0c;通过VB6注册的Activex DLL…

一文速学-让神经网络不再神秘,一天速学神经网络基础(一)

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…

C语言刷题(13)

第一题 第二题 第三题 第四题 第五题 第六题 第七题 注意 1.nsqrt(n)&#xff0c;sqrt本身不会将n开根 2.初始化已经令sumn了&#xff0c;故相加的个数为m-1次

K8S用户管理体系介绍

1 K8S账户体系介绍 在k8s中&#xff0c;有两类用户&#xff0c;service account和user&#xff0c;我们可以通过创建role或clusterrole&#xff0c;再将账户和role或clusterrole进行绑定来给账号赋予权限&#xff0c;实现权限控制&#xff0c;两类账户的作用如下。 server acc…

小航助学GESP_C++一级模拟测试卷第2套(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSD…

大学生创业出路【第二弹】科创训练营

目录 &#x1f680;一、我从哪里了解到的训练营 &#x1f680;二、训练营里学习和日常 &#x1f50e;学习 &#x1f50e;环境和设备 &#x1f50e;遇到的人 &#x1f50e;团队记录视频 &#x1f680;三、感悟 ​​​​个人主页&#xff1a;一天三顿-不喝奶茶&#x1f39…

JVM元空间溢出的排除思路

背景&#xff1a; java的应用我们为了防止元空间的无限扩展&#xff0c;一般都会设置MaxMetaSpace参数&#xff0c;一般来说只要这个值是512M或者1G左右就足够了&#xff0c;不过今天遇到一个meta空间溢出问题&#xff0c;简单记录下排除的思路 meta元空间溢出 最开始的现象…

实验六 u-boot-2013.01移植

【实验目的】 了解u-boot 的代码结构及移植的基本方法 【实验环境】 ubuntu 14.04发行版FS4412实验平台交叉编译工具arm-none-linux-gnueabi- 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行 【实验步骤】 一、建立自己的平台 下载uboot源码 在uboo…

【网络】DNS | ICMP | NAT | 代理服务器

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 前面几篇文章虽然讲介绍了整个网络通信的协议栈&#xff0c;我们也知道了完整的网络通信过程&#xff…

【⑬MySQL | 数据类型(一)】简介 | 整数 | 浮点 | 定点类型

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL数据类型简介 | 整数 | 浮点 | 定点类型的分享✨ 目录 前言0.数据类型简介1 整数类型2 浮点类型3 定点类型4 日期/时间类型总结 0.数据类型简介 数据类型&#xff08;data_type&#xff09;是指系统中所允许的…

LRU淘汰策略执行过程

1 介绍 Redis无论是惰性删除还是定期删除&#xff0c;都可能存在删除不尽的情况&#xff0c;无法删除完全&#xff0c;比如每次删除完过期的 key 还是超过 25%&#xff0c;且这些 key 再也不会被客户端访问。 这样的话&#xff0c;定期删除和堕性删除可能都彻底的清理掉。如果…

百度许少辉著Baidu《乡村振兴战略下传统村落文化旅游设计》图书馆新书通报

百度许少辉著Baidu《乡村振兴战略下传统村落文化旅游设计》图书馆新书通报

AWS SDK 3.x for .NET Framework 4.0 可行性测试

前言 为了应对日益增长的网络安全挑战, 越来越多的互联网厂商已经陆续开始或者已经彻底停止了对 SSL 3 / TLS 1.0 / TLS1.1 等上古加密算法的支持. 而对于一些同样拥有悠久历史的和 AWS 服务相关联的应用程序, 是否可以通过仅更新 SDK 版本的方式来适应新的环境. 本文将以 Win…

揭开pkill的秘密:在Linux中杀死进程的完整指南

揭开pkill的秘密&#xff1a;在Linux中杀死进程的完整指南 一、前言二、进程管理基础知识2.1、什么是进程&#xff1f;2.2、进程管理的原理和目的2.3、进程状态和标识符 三、理解Linux pkill命令3.1、pkill命令的概述和作用3.2、与kill命令的对比3.3、常见的pkill命令选项和参数…

PCAP01介绍和STM32模拟SPI驱动

一.芯片介绍 Pcap01是德国acam公司设计的一款革命性的电容测量芯片。该芯片 内部有DSP计算单元&#xff0c;可以直接将电容元件接到Pcap01芯片&#xff0c;然后芯片计算出容值大小&#xff0c;通过SPI总线将电容容值数据传送给CPU&#xff0c;电容测量完全数字化。 二,测量原…