C++:二叉搜索树的底层模拟实现


 

概念:

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

搜索二叉树的操作:

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};
  • 二叉搜索树需要满足左子树比根小,右子树比根大,每一棵树,每一颗子树都需要满足这个条件
  • 二叉搜索树使用中序遍历后,得出的遍历结果一定是一个升序序列
  • 二叉搜索树的的查询操作雷素与二分查找,但是却比二分查找要来的简单,因为搜索二叉树的删除和插入操作比二分查找更为的简单,且并不需要二分查找一样,当插入和删除后必须要在经历一次排序操作。 
  • 最后二叉搜索树是不允许进行节点内部的value 也就是节点数值的修改,这是因为二叉搜索树的排序操作和树的结构是因为节点的value进行链接和形成的

节点结构:

	template<class K>//struct BinarySearchTreeNodestruct BSTreeNode{BSTreeNode<K>* _left;//指向左子树指针BSTreeNode<K>* _right;//指向右子树指针K _key;//用来进行排序的value数值BSTreeNode(const K& key)//节点的初始化列表:_left(nullptr), _right(nullptr), _key(key){}};

插入操作:

 二叉搜索树的插入 插入的具体过程如下:

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

因为插入时需要保证插入之后还是二叉搜索树,所以插入操作需要进行一次的对比, 例如插入数字5,如果需要插入数字5,那么就需要进行依次的对比!

也就是和每一个节点进行对比,从根节点开始,比对比的节点小往左边移动,比对比的节点大往右移动,直到走到最后一个节点,走向空,需要记住每次插入的节点(非重复)最后都是会插入一个空的位置。

同时还需要注意,当插入的数据已经在树中存在了,而就需要注意搜索树中不会冗余,也就是插入一个相同的元素是不允许在同一个树中出现两次,这是不允许的!除非需要一些扩展。

		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;}

查找函数:

 二叉搜索树的查找:

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

b、最多查找高度次,走到到空,还没找到,这个值不存在。

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;}

删除函数:

 删除操作分为三种情况/两种操作:

a、删除的节点他的左子树是空的,则他的父节点要指向他的右子树,同理删除的节点他的右子树是空的,他的父节点要指向他的左子树

b、叶子节点,不过叶子节点可以和第一种情况相结合,让父节点指向删除的叶子节点的右子树

c、左右子树都不为空的:需要使用替换法,找该节点的右子树最小节点或者左子树最大节点

这里找右子树的最小节点。

操作一:情况a、情况b

删除的节点他的左子树是空的,则他的父节点要指向他的右子树,同理删除的节点他的右子树是空的,他的父节点要指向他的左子树。

同时这里还会诞生一个问题,被删除节点的左子树是空的,那父节点指向这个节点的右子树,但是是父节点的那一个指针指向呢?是右指针还是左指针?

 需要进行额外的判断,判断删除的节点是父节点的左子树还是右子树,左子树就左指针,右子树就右指针。

且还有一个细节:例如左边为空时,被删除节点的父亲点的要进行判断,判断删除的节点是父亲节点的左节点还是右节点,但是如果我们删除的是根节点,就如上图所示.

这种就需要提前进行判断,如果当前的节点是一个根节点,且左边为空时,那么根节点就需要进行替换,替换成这个根节点的右子树的头一个节点,如果是右边为空,那么这个根节点就需要替换成根节点的左子树的头一个节点。

				else{// 删除// 左为空,父亲指向我的右if (cur->_left == nullptr)//判断被删除的节点是否是他的左子树为空{//if(parent == nullptr)是否为根节点的判断if (cur == _root){//为根节点,那么根节点替换成根节点的右子树_root = cur->_right;}else{//不算根节点则进行父亲节点的指向判断if (cur == parent->_left){
//如果当前节点是父亲节点的左子树,那么就使用父亲节点的左指针指向这个节点的右子树parent->_left = cur->_right;}else{
//如果当前节点是父亲节点的右子树,那么就使用父亲节点的右指针指向这个节点的右子树parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//if(parent == nullptr)//是否是根节点的判断if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}

操作二:

 左右子树都不为空的:需要使用替换法,找该节点的右子树最小节点或者左子树最大节点,这里找右子树的最小节点。

找到这个被删除节点的右子树,然后进入右子树后一直往左边走,找到被删除节点的右子树的 最小的数,而删除操作则需要吧要删除的节点和最小数替换(交换操作)

但是有产生一个问题,之后要删除的话,可能找不到要删除的节点,所以我们还需要一个最小节点的父亲节点,进行指向操作,所以在找右子树最小节点的时候,还得跟更新他的父节点。

同时删除后让父亲节点的左指针指向空(这里删除的节点是叶子节点没有字节的,所以删除节点的指向是null)

但是这种只能因对常规删除,如果删除的的节点中他的右子树是一个叶子节点呢,又或者,它是根节点呢:

就如上图这种情况,这种情况下删除根节点,那么右子树的最小节点就是根的右孩子节点

同时在我们的代码中,交换删除后,我们让父亲节点的左指针,指向被删除节点的右子树(常规操作是最小右节点的父亲节点指向空,这里的最小右节点刚好是根节点的有孩子),那么放在上图情况会直接崩掉,所以还需要进行一个判断

最后这个判断的前者是判断是否是正常情况,在正常情况中,最小右子树节点的父节点只是一个普通的节点,且最小右子树节点的父节点的左边一定是最小右子树节点,所以可以让父节点指向最小右字数节点的 右子树

而非常情况就是如上图这种情况,根节点的右孩子节点就是根节点的最小右子树节点,这种非常情况下,根节点的最小右子树节点在右边,所以需要让根节点的右指针指向最小右子树节点的 右子树。

else{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}
完成的删除函数代码: 
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(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_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 << " ";_InOrder(root->_right);}

需要注意,这里中序遍历这样写的原因是因为root根节点是私有的原因,所以在外是调不动中序遍历函数的,所以需要把中序遍历函数进行套用,放在私有成员内部,在外部套用一层调用即可使用。 


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

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

相关文章

无人机+集群组网:无人机无线组网巡检方案

随着无人机技术的快速发展&#xff0c;其在各种巡检场景中的应用日益广泛。无人机能够快速部署、灵活飞行&#xff0c;并且搭载多种传感器设备&#xff0c;为巡检工作提供了全新的视角和手段。然而&#xff0c;单一的无人机作业受限于通信距离和覆盖范围&#xff0c;难以满足大…

匹配网络(Matching Networks)和原型网络(Prototypical Networks):区别详解

匹配网络&#xff08;Matching Networks&#xff09;和原型网络&#xff08;Prototypical Networks&#xff09; 匹配网络与原型网络&#xff1a;区别详解匹配网络&#xff08;Matching Networks&#xff09;核心特点&#xff1a;应用场景&#xff1a; 原型网络&#xff08;Pro…

HTML_CSS学习:CSS盒子模型

一、CSS中常用的长度单位 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>CSS中常用的长度单位</title><style>html{font-size: 40px;}#d1{/*第一种长度单位&…

Web端重叠路径可视化

近几年来&#xff0c;由于信息技术的发展&#xff0c;大数据成为了这个时代的代名词之一&#xff0c;“数据可视化”风靡一时。得益于HTML5提供的新标签“canvas”&#xff0c;Web端也能分“数据可视化”一杯羹。 随着越来越多的可视化方案和需求&#xff0c;需要解决问题也越来…

Redis-三主三从高可用集群搭建

正式搭建之前&#xff0c;注意事项&#xff08;坑&#xff09;提前放到最开始&#xff0c;也可以出问题回来看&#xff0c; &#xff08;1&#xff09;第二步中最好将配置文件中的logfile自定义一个目录&#xff0c;以便于在第五步中启动出错的时候迅速定位错误。 &#xff0…

Qt_介绍_环境安装_创建新项目_实现helloworld_坐标_1

文章目录 一、Qt是什么二、Qt的发展史三、Qt支持的平台四、Qt版本五、Qt的优点六、Qt的应用场景七、Qt开发环境&#xff0c;需要按照3个部分1.C编译器&#xff08;gcc,cl.exe....不是Visual Studio&#xff09;2.Qt SDK3.需要有一个Qt的集成开发环境&#xff08;IDE&#xff09…

不尝试一下?计算机领域两大赛事来了!!

前言 最近&#xff0c;熊二新来的同事小强比较关注国内的一些赛事信息。这不&#xff0c;近期有两大赛事。这两大赛事&#xff0c;主要还是面向高校学生的。一个是搞网络安全方向的: 第二届京麒CTF挑战赛&#xff0c;另一个是搞数据库方向的: 2024年全国大学生计算机系统能力大…

指令寻址——顺序寻址、跳跃寻址

目录 一、概述 1.定义 2.寻址方式分类 3.形式地址、物理地址 二、指令寻址 1、顺序寻址方式 2、跳跃寻址方式 一、概述 1.定义 寻址方式解决的是指如何在指令中表示一个操作数的地址&#xff0c;如何用这种表示得到操作数、或怎样计算出操作数的地址。 2.寻址方式分类…

题目:方格取数[Easy]

问题描述&#xff1a; 解题思路&#xff1a; 可以使用动态规划&#xff0c;建立dp[i][j][x]&#xff0c;表示&#xff08;1&#xff0c;1&#xff09;到&#xff08;i&#xff0c;j&#xff09;且其积的余数为x的情况下的方案数。时间复杂度为(n^2) * k。 AC代码&#xff1a; …

C语言/数据结构——每日一题(移除链表元素)

一.前言 今天在leetcode刷到了一道关于单链表的题。想着和大家分享一下。废话不多说&#xff0c;让我们开始今天的知识分享吧。 二.正文 1.1题目要求 1.2思路剖析 我们可以创建一个新的单链表&#xff0c;然后通过对原单链表的遍历&#xff0c;将数据不等于val的节点移到新…

RateLimiter 限流 —— 通过切面对单个用户进行限流和黑名单处理

关于登录的安全性管理有较多的手段&#xff0c;包括&#xff1b;设备信息、IP信息、绑定的信息、验证码登各类方式。不过在一些网页版的登录中&#xff0c;如果有人想办法把你的验证码给我&#xff0c;我就可以登录你的账户&#xff0c;查看你的数据。对于一些不法分子通过让你…

【系统架构师】-选择题(十一)

1、紧耦合多机系统一般通过&#xff08;共享内存&#xff09;实现多机间的通信。对称多处理器结构&#xff08;SMP&#xff09;属于&#xff08; 紧耦合&#xff09;系统。 松耦合多机系统又称间接耦合系统,—般是通过通道或通信线路实现计算机间的互连。 2、采用微内核的OS结构…

数据库基础--MySQL多表查询之联表查询

联表查询 定义&#xff1a;多张表联合在一起查询&#xff0c;例如学生信息与学生班级表、部门与员工表 创建两张表&#xff0c;主表与从表 CREATE TABLE TestMain(id INT Not NULL AUTO_INCREMENT,nameVARCHAR(10),introduction VARCHAR(255),PRIMARY KEY(id) ); CREATE TAB…

短视频矩阵系统ai剪辑 矩阵 文案 无人直播四合一功能核心独家源头saas开发

抖去推矩阵AI小程序是一款针对短视频平台的智能创作和运营工具&#xff0c;它具有以下功能特点&#xff1a; 1.批量视频生成&#xff1a;抖去推可以在短时间内生成大量视频&#xff0c;帮助商家快速制作出适合在短视频平台上推广的内容 2.全行业覆盖&#xff1a;适用于多个行业…

Golang数组与切片

文章目录 数组数组介绍数组的定义方式访问与修改数组元素遍历数组元素数组指针 切片切片介绍切片的定义方式访问与修改切片元素添加切片元素切片的拷贝遍历切片元素string的切片 数组 数组介绍 数组介绍 在Go中&#xff0c;数组是一个由固定长度的特定类型元素组成的序列&…

第十五届蓝桥杯省赛大学B组(c++)

很幸运拿了辽宁赛区的省一,进入6月1号的国赛啦... 这篇文章主要对第十五届省赛大学B组(C)进行一次完整的复盘,这次省赛2道填空题6道编程题: A.握手问题 把握手情景看成矩阵: 粉色部分是7个不能互相捂手的情况 由于每个人只能和其他人捂手, 所以黑色情况是不算的 1和2握手2和…

QT防止自研软件被复制的基本操作(二)

参考一 自研软件为了防止被人任意复制传播&#xff0c;需要设置注册使用模式。基本原理&#xff1a;通过计算机的特异性编号&#xff0c;加上自己的编码&#xff0c;使用加密算法算出一个生成码。 一、计算机的特异性编号 硬盘的编号&#xff1a;最后一块硬盘的编号就行&#…

【C语言】/*函数栈帧的创建和销毁*/

目录 前言 一、知识补充 二、分析创建和销毁的过程 三、前言问题回答 前言 本篇主要讨论以下问题&#xff1a; 1. 编译器什么时候为局部变量分配的空间 2. 为什么局部变量的值是随机的 3. 函数是怎么传参的&#xff0c;传参的顺序是怎样的 4. 形参和实参是什么关系 5. 函数…

模型 SOP(标准操作程序)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。标准化流程&#xff0c;提质增效&#xff0c;保障合规。 1 SOP的应用 1.1 餐厅日常卫生清洁标准操作程序&#xff08;SOP&#xff09; 下面展示一个餐厅如何通过SOP确保清洁工作的标准化&#xff0c…

基于OpenCv的图像傅里叶变换

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…