c++中的二叉搜索树

一·概念:

静图展示:

动图展示:

①左子树不为空,则左子树节点值小于根节点值。

②右子树不为空,则右子树节点值大于根节点值。

③左右子树均为二叉搜索树。

④对于它可以插入相等的也可以插入不相等的,这里如果插入的话一般执行的就是覆盖操作,也就是不允许插入如:

注:以二叉树为底层的容器:map(key_value模型),set(key模型),multimap,multiset,其中前两个不支持插入相等的值,而后两者便支持。

二·性能分析:

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为:O(log2 N)

最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为:O( N)

所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为:O(N)

下面是它的缺点:插入的数据在它中应该是有序的,而且要知道这样会可以随机访问里面的数据,那么插入与删除就变得复杂了,因此引出后面需要的平衡二叉树。

三·实现步骤:

下面把它的主体分为三点:插入,删除(复杂点),查找,(不支持修改,因为会改变这棵树的性质)。

3·1插入:

这里比较简单,就是找比这个节点值大就往右走,小就往左走,直到走到空,就可以开辟节点并插入,但是问题就是连接起来,因此需要保存上一个也就是parent节点:

bool insert(const k& key) {//头结点直接加:if (_root == nullptr) {_root = new bsnode<k>(key);return true;}else {//找到这个位置(通过搜索比较)bsnode<k>* parent = nullptr;//为了连接:bsnode<k>* 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 bsnode<k>(key);//完成连接操作:if (parent->_key > key) parent->_left = cur;else parent->_right = cur;return true;}}

3·2删除:

当然大体框架到了删除这一步一般就是难点了,因为考虑的情况很多,下面我们画图把它要考虑的情况画出:

代码:

bool erase(const k& key) {bsnode<k>* parent = nullptr;bsnode<k>* cur = _root;if (_root == nullptr) return false;//找到这个key的位置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 && cur->_right == nullptr) {//这里由于parent左右指针还和cur连一起,注意置空if (parent == nullptr) {delete cur;}else {if (parent->_left == cur) parent->_left = nullptr;else if (parent->_right == cur) parent->_right = nullptr;delete cur;}//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)}//第二种情况:else if (cur->_left == nullptr) {if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用elseelse {if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就//要考虑parent是否为空else if (parent->_right == cur) parent->_right = cur->_right;}delete cur;}//第三种情况:else if (cur->_right == nullptr) {if (parent == nullptr) _root = cur->_left;else {if (parent->_left == cur) parent->_left = cur->_left;else if (parent->_right == cur) parent->_right = cur->_left;}delete cur;}第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大else {bsnode<k>* er_right_min_p = cur;//它的父亲节点bsnode<k>* er_right_min = cur->_right;//那个被替换要删除的节点while (er_right_min->_left) {er_right_min_p = er_right_min;er_right_min = er_right_min->_left;//找到这个被替代节点}cur->_key = er_right_min->_key;//完成覆盖//下面判断这个节点是父亲左节点还是右节点连接的:if (er_right_min_p->_left == er_right_min)er_right_min_p->_left = er_right_min->_right;else if (er_right_min_p->_right == er_right_min)er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接delete er_right_min;}return true;}}return false;
}

 

3·3查找:

这里由于不考虑相同值的情况,因此就按照它的性质去查找即可:

bool find(const k& key) {//这里也可搞成返回bsnode*类型方便使用bsnode<k>* cur = _root;while (cur) {if (cur->_key > key) cur = cur->_left;else if (cur->_key < key) cur = cur->_right;else return true;}return false;
}

四·应用(key与key_value):

4·1key模型:

上面提到了set容器就是key模型:

简单来说就是key不是决定了树的性质排列,然后值是对key来完成增删查等操作。

场景1:小区车牌号放行系统简单模拟。

场景2:检查单词是否出现拼写错误。

实现代码:

namespace K {template< typename k>struct bsnode {k _key;bsnode<k>* _left;bsnode<k>* _right;bsnode(const k& key) :_key(key),_left(nullptr),_right(nullptr) {}};template< typename k>class bstree {public:bstree() = default;bstree(bstree<k>& t) {_root = copy(t._root);//这里利用copy函数搞了个临时对象,为了不让swap操作这个root而是操作的临时对象}bstree<k> operator=(bstree<k> t) {swap(_root, t._root);//t自定义类型会调用拷贝构造然后swap交换的事这个临时对象,t不受影响return *this;}bool insert(const k& key) {//头结点直接加:if (_root == nullptr) {_root = new bsnode<k>(key);return true;}else {//找到这个位置(通过搜索比较)bsnode<k>* parent = nullptr;//为了连接:bsnode<k>* 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 bsnode<k>(key);//完成连接操作:if (parent->_key > key) parent->_left = cur;else parent->_right = cur;return true;}}bool find(const k& key) {//这里也可搞成返回bsnode*类型方便使用bsnode<k>* 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 k& key) {bsnode<k>* parent = nullptr;bsnode<k>* cur = _root;if (_root == nullptr) return false;//找到这个key的位置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 && cur->_right == nullptr) {//这里由于parent左右指针还和cur连一起,注意置空if (parent == nullptr) {delete cur;}else {if (parent->_left == cur) parent->_left = nullptr;else if (parent->_right == cur) parent->_right = nullptr;delete cur;}//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)}//第二种情况:else if (cur->_left == nullptr) {if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用elseelse {if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就//要考虑parent是否为空else if (parent->_right == cur) parent->_right = cur->_right;}delete cur;}//第三种情况:else if (cur->_right == nullptr) {if (parent == nullptr) _root = cur->_left;else {if (parent->_left == cur) parent->_left = cur->_left;else if (parent->_right == cur) parent->_right = cur->_left;}delete cur;}第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大else {bsnode<k>* er_right_min_p = cur;//它的父亲节点bsnode<k>* er_right_min = cur->_right;//那个被替换要删除的节点while (er_right_min->_left) {er_right_min_p = er_right_min;er_right_min = er_right_min->_left;//找到这个被替代节点}cur->_key = er_right_min->_key;//完成覆盖//下面判断这个节点是父亲左节点还是右节点连接的:if (er_right_min_p->_left == er_right_min)er_right_min_p->_left = er_right_min->_right;else if (er_right_min_p->_right == er_right_min)er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接delete er_right_min;}return true;}}return false;}~bstree(){destroy(_root);}void inorder() {_inorder(_root);cout << endl;}private://中序遍历:void _inorder(const bsnode<k>* root) {if (root == nullptr) return;_inorder(root->_left);cout << root->_key << " ";_inorder(root->_right);}//后序删除:void destroy(bsnode<k>* root) {if (root == nullptr) return;destroy(root->_left);destroy(root->_right);delete root;root = nullptr;}//前序安装:bsnode<k>* copy(bsnode<k>* root) {//这里递归完成:假设copy的递归已经完成到最后一步,即只需要按照根节点把它的左右子树连起来即可if (root == nullptr) return nullptr;bsnode<k>* newnode = new bsnode<k>(root->_key);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}bsnode<k>* _root = nullptr;};
}

4·2key_value模型:

key包含了性质,而value不改变性质只是一个标志,可以更改,而查找和删除还是根据key来决定。

场景1:单词的英汉互译。

场景2:无人停车时长收费计算系统模拟。

场景3:一些物品的计数统计。

这里如果要是实现只需要再key模型代码完成一些对value的改变即可:

namespace K_Value {template< typename k,typename v>struct bsnode {k _key;v _value;bsnode<k,v>* _left;bsnode<k,v>* _right;bsnode(const k& key, const v& value) :_key(key),_value(value),_left(nullptr),_right(nullptr) {}};template< typename k, typename v>class bstree {public:bstree() = default;bstree(bstree<k,v>& t) {_root = copy(t._root);//这里利用copy函数搞了个临时对象,为了不让swap操作这个root而是操作的临时对象}bstree<k,v> operator=(bstree<k,v> t) {swap(_root, t._root);//t自定义类型会调用拷贝构造然后swap交换的事这个临时对象,t不受影响return *this;}bool insert(const k& key, const v& value) {//头结点直接加:if (_root == nullptr) {_root = new bsnode<k,v>(key,value);return true;}else {//找到这个位置(通过搜索比较)bsnode<k,v>* parent = nullptr;//为了连接:bsnode<k,v>* 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 bsnode<k,v>(key,value);//完成连接操作:if (parent->_key > key) parent->_left = cur;else parent->_right = cur;return true;}}bsnode<k,v>* find(const k& key) {bsnode<k,v>* cur = _root;while (cur) {if (cur->_key > key) cur = cur->_left;else if (cur->_key < key) cur = cur->_right;else return cur;}return nullptr;}bool erase(const k& key, const v& value) {bsnode<k,v>* parent = nullptr;bsnode<k,v>* cur = _root;if (_root == nullptr) return false;//找到这个key的位置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 && cur->_right == nullptr) {if (parent == nullptr) {delete cur;}else {if (parent->_left == cur) parent->_left = nullptr;else if (parent->_right == cur) parent->_right = nullptr;delete cur;}//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)}//第二种情况:else if (cur->_left == nullptr) {if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用elseelse {if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就//要考虑parent是否为空else if (parent->_right == cur) parent->_right = cur->_right;}delete cur;}//第三种情况:else if (cur->_right == nullptr) {if (parent == nullptr) _root = cur->_left;else {if (parent->_left == cur) parent->_left = cur->_left;else if (parent->_right == cur) parent->_right = cur->_left;}delete cur;}第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大else {bsnode<k,v>* er_right_min_p = cur;//它的父亲节点bsnode<k,v>* er_right_min = cur->_right;//那个被替换要删除的节点while (er_right_min->_left) {er_right_min_p = er_right_min;er_right_min = er_right_min->_left;//找到这个被替代节点}cur->_key = er_right_min->_key;//完成覆盖//下面判断这个节点是父亲左节点还是右节点连接的:if (er_right_min_p->_left == er_right_min)er_right_min_p->_left = er_right_min->_right;else if (er_right_min_p->_right == er_right_min)er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接delete er_right_min;}return true;}}return false;}~bstree(){destroy(_root);}void inorder() {_inorder(_root);cout << endl;}private://中序遍历:void _inorder(const bsnode<k,v>* root) {if (root == nullptr) return;_inorder(root->_left);cout << root->_key << ":"<<root->_value;_inorder(root->_right);}//后序删除:void destroy(bsnode<k,v>* root) {if (root == nullptr) return;destroy(root->_left);destroy(root->_right);delete root;root = nullptr;}//前序安装:bsnode<k,v>* copy(bsnode<k,v>* root) {//这里递归完成:假设copy的递归已经完成到最后一步,即只需要按照根节点把它的左右子树连起来即可if (root == nullptr) return nullptr;bsnode<k>* newnode = new bsnode<k,v>(root->_key);newnode->_left = copy(root->_left);newnode->_right = copy(root->_right);return newnode;}bsnode<k,v>* _root = nullptr;};}

到此为止希望对你对二叉搜索树的理解有点帮助,感谢支持!!! 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

JavaSE语法阶段复习知识整理3之封装

文章目录 一、封装1.1 封装的概念1.2 访问限定符1.3封装扩展之包 二、static成员2.1static关键字的引入2.2静态成员变量初始化2.3访问静态成员变量2.4用实际问题加深静态成员变量的理解2.5静态成员变量的总结要点2.6静态成员方法的总结要点 三、代码块3.1普通代码块3.2构造代码…

QXDM 如何更新软件?

如何更新QXDM等高通软件&#xff1f;之前做过这个事情&#xff0c;但过几个月给别人讲的时候就忘记了&#xff0c;特做如下记录。 一. 背景知识&#xff1a; 1. QXDM 依赖于Qualcomm package Managers 3(QPM in short)。 目前的时间是2024年9月15日&#xff0c;但不知从何…

学习笔记JVM篇(一)

1、类加载的过程 加载->验证->准备->解析->初始化->使用->卸载 2、JVM内存组成部分&#xff08;HotSpot&#xff09; 名称作用特点元空间&#xff08;JDK8之前在方法区&#xff09;用于存储类的元数信息&#xff0c;例如名称、方法名、字段等&#xff1b;…

[苍穹外卖]-09Spring Task定时任务

Spring Task spring Task是spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 只要是需要定时处理的场景都可以使用Spring Task定时任务框架 cron表达式就是一个字符串,可以定义任务触发的时间 构成规则: 分为6或7个域, 由空格隔开,每个域代表一个含义每…

Java 全面指南:从入门到精通

目录 1. 引言 Java 的背景 Java 的起源及历史发展 主要的应用场景 Java 的核心特性 面向对象 跨平台性&#xff08;JVM 的角色&#xff09; 自动内存管理与垃圾回收机制 Java 版本与发展历程 Java SE 8, 11, 17 等主要版本特性 新增功能概述&#xff08;如 Lambda 表…

SpringBoot新技能:零停机更新代码

在个人或者企业服务器上&#xff0c;总归有要更新代码的时候&#xff0c;普通的做法必须先终止原来进程&#xff0c;因为新进程和老进程端口是一个&#xff0c;新进程在启动时候&#xff0c;必定会出现端口占用的情况&#xff0c;但是&#xff0c;还有黑科技可以让两个SpringBo…

【机器学习】--- 深度学习中的注意力机制

深度学习中的注意力机制 在深度学习领域&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;已经成为近年来最受瞩目的研究热点之一。它不仅提升了现有模型的性能&#xff0c;更启发了全新的网络结构&#xff0c;如Transformer模型。注意力机制被广泛应用于自…

c语言中的局部跳转以及全局跳转

一、前言 在c语言中&#xff0c;当我们在处理某些异常情况的时候&#xff0c;经常会使用goto语句来进行跳转。goto用起来很方便&#xff0c;但可能很多人都不知道&#xff0c;goto只能在一个函数里面跳转&#xff0c;并不能够跨函数跳转。本文将介绍能够跨函数跳转的接口setjm…

升级VMware

1、vm17pro安装包 VMware Workstation 17 Pro软件下载&#xff1a; 官网下载&#xff1a;Download VMware Workstation Pro 2、点击下一步更改地址 3、注册码 VMware Workstation 17 Pro注册码&#xff1a; 4A4RR-813DK-M81A9-4U35H-06KND 4、打开虚拟机 注&#xff1a; 升…

ip地址数字范围是多少?ip地址四段数字的含义是什么

IP地址&#xff0c;作为互联网上的唯一标识&#xff0c;是由一串数字组成的。这些数字不仅代表了设备的网络位置&#xff0c;还蕴含了丰富的信息。本文将深入探讨IP地址的数字范围以及四段数字的具体含义。 一、IP地址数字范围是多少 IP地址由四段数字组成&#xff0c;每一段数…

JavaEE:文件内容操作(二)

文章目录 文件内容操作读文件(字节流)read介绍read() 使用read(byte[] b) 使用 read(byte[] b, int off, int len) 使用 写文件(字节流)write介绍write(int b) 使用write(byte[] b) 使用write(byte[] b, int off, int len) 使用 读文件(字符流)read() 使用read(char[] cbuf) 使…

基于python+django+vue的鲜花商城系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…

如何做系统架构?从动态系统思考的角度

在动态系统思考的背景下&#xff0c;系统架构不再只是一个静态的、结构化的设计&#xff0c;而是一个随着时间推移、基于不同要素互动产生涌现行为的动态过程。系统架构师的任务不仅仅是定义系统的形态和结构&#xff0c;更是通过剖析系统的互动网络、功能涌现和使用场景&#…

UVA1395 Slim Span(最小生成树)

*原题链接*(洛谷) 非常水的一道题。看见让求最小边权差值的生成树&#xff0c;很容易想到kruskal。 一个暴力的想法是以每条边为最小边跑一遍kruskal&#xff0c;然后统计答案。时间复杂度&#xff0c;再看题中很小的数据范围和3s的时限。最后还真就过了。 不过我天真的想了…

三维点云处理(C++)学习记录——PDAL

一、OSGeo4W简概 OSGeo4W是一个基于Windows系统&#xff08;版本7-11&#xff09;的开源地理软件二进制包发布平台。OSGeo4W包括开源GIS桌面应用程序&#xff08;QGIS、GRASS GIS&#xff09;、地理空间库&#xff08;PROJ、GDAL/OGR、GEOS、SpatiaLite、SAGA GIS&#xff09;、…

鸿蒙开发笔记_电商严选02_登录页面跳转到我的页面、并传值

鸿蒙开发笔记整理,方便以后查阅! 由于上班较忙,只能抽空闲暇时间,快速整理更新中。。。 登录页面跳转到我的页面、并传值 效果图 我的设置页面 /*** 我的设置页面*/ import CommonConstants from ./CommonConstants import ItemData from ./ItemData import DataModel fr…

面试官问:你为什么对这个职位感兴趣?

当面试官问到你为什么对某个职位感兴趣时&#xff0c;你的回答应该反映出你对该职位的热情&#xff0c;以及你如何能够为公司带来价值。 重点&#xff1a;在面试前一定要去研究下这家公司&#xff0c;包括他们的团队&#xff0c;文化&#xff0c;产品&#xff0c;服务等各个方…

55 mysql 的登录认证流程

前言 这里我们来看一下 mysql 的认证的流程 我们这里仅仅看 我们最常见的一个 认证的处理流程 我们经常会登录的时候 碰到各种异常信息 认证失败的大体流程 大概的流程是这样 客户端和服务器建立连接之后, 服务器向客户端发送 salt 然后 客户端根据 salt 将客户端传入的…

不同的二叉搜索树

题目 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5示例 2&#xff1a; 输入&#xff1a;n 1 输出&#xff…

运行QWen2-1.5b模型时报错“RuntimeError: cutlassF: no kernel found to launch!”

运行QWen2-1.5b模型时报错“RuntimeError: cutlassF: no kernel found to launch!” #问题&#xff1a;成功加载QWen2-1.5b模型&#xff0c;但是推理时 “model.generate( model_inputs.input_ids, top_pself.top_p, max_new_tokens512 )时”&#xff0c;报错“RuntimeError: …