当前位置: 首页 > news >正文

C++搜索二叉树

本章主要是二叉树的进阶部分,学习搜索二叉树可以更好理解后面的mapset的特性。

1.二叉搜索树概念

二叉搜索树的递归定义为:非空左子树所有元素都小于根节点的值,非空右子树所有元素都大于根节点的值,而左右子树也是二叉搜索树。

在这里插入图片描述

2.二叉搜索树实现

#include <iostream>
#include <string>
using namespace std;template<typename K>//这里更加习惯写K,也就是关键值key的类型
struct BinarySearchTreeNode
{BinarySearchTreeNode<K>* _left;BinarySearchTreeNode<K>* _right;K _key; BinarySearchTreeNode(K key = K()) : _key(key), _left(nullptr), _right(nullptr) {}
};template<typename K>
class BinarySearchTree
{typedef BinarySearchTreeNode<K> Node;
public://BinarySearchTree() : _root(nullptr) {}BinarySearchTree() = default;//强制编译器生成默认的构造函数BinarySearchTree(const BinarySearchTree<K>& b){_root = copy(b._root);}BinarySearchTree<K>& operator=(BinarySearchTree<K> b)//b拷贝了一份{swap(_root, b._root);return *this;}~BinarySearchTree(){destroy(_root);}//1.插入bool insert(const K& key){/*对于第一个插入的节点就是根节点。至于数据冗余,我在这里定义不允许数据冗余,也就是不允许出现重复的数据节点。这样的搜索二叉树会受到数据先后顺序插入的影响(您也可定义允许)*///1.查看是否根节点if (_root == nullptr){_root = new Node(key);return true;}//2.寻找存放的位置Node* parent = nullptr;//存放root的父节点Node* root = _root;//遍历,从根节点开始while (root)//直到空为止{parent = root;if (root->_key < key) {root = root->_right;}else if(root->_key > key){root = root->_left;}else//root->_key == key{return false;}}//3.插入节点及数据root = new Node(key);if (parent->_key < key)//注意不可以直接赋值给root,不仅内存泄露还连接不上节点{parent->_right = root;}else{parent->_left = root;}return true;}bool insertR(const K& key){return _insertR(_root, key);}//2.删除bool erase(const K& key){/*寻找被删除的节点,删除后,如果是单子节点还好,如果是多子节点就需要找到一个托孤后依旧满足二叉搜索树性质的节点,因此删除有两种情况:A.被删除节点是叶子节点 或者 被删除节点的左或右孩子为空,直接将孩子节点替换被删除节点即可B.被删除节点拥有两个子节点,取右子树中最小的节点替代被删除的节点(当然也可以取左子树的最大节点)b1.最小节点没有右孩子,最小节点直接替代被删除节点,并且将最小节点的空孩子节点交给父节点领养b2.最小节点存在右孩子,最小节点直接替代被删除节点,并且将最小节点的右孩子节点交给父节点领养最后还需要注意删除根节点,根节点没有父节点的问题*/Node* parent = nullptr;Node* cur = _root;//1.寻找节点while (cur){if (cur->_key < key){parent = cur;//不可以和下一个if语句共用,会出现cur和parenat的情况,例如:test_1()中删除10的时候cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{//2.删除节点(找到了)if (cur->_left == nullptr)//2.1.左为空{if (parent == nullptr)//避免cur是根节点,没有父节点,例如:test_1()中删除11的时候{_root = cur->_right;delete cur;return true;}if (parent->_left == cur){parent->_left = cur->_right;}else//parent->_right == cur{parent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr)//2.2.右为空{if (parent == nullptr){_root = cur->_left;delete cur;return true;}if (parent->_left == cur){parent->_left = cur->_left;}else//parent->_right == cur{parent->_right = cur->_left;}delete cur;}else//2.3.左右均不为空,取左子树中最大的或者取右子树中最小的节点替代被删除的节点{Node* pminRight = cur;//注意不能为nullptr,因为有可能出现不进循环的情况Node* minRight = cur->_right;//我们选择找右数最小节点while (minRight->_left != nullptr)//找到最左节点,但是需要注意这个最左节点如果有右树,那就需要最左节点的父节点接管{pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;//替换相当于删除if (pminRight->_left == minRight)//最左节点的父节点托管最左节点的右树,注意可能有两种情况{pminRight->_left = minRight->_right;}else if (pminRight->_right == minRight)//最左节点的父节点托管最左节点的右树,注意可能有两种情况{pminRight->_right = minRight->_right;}delete minRight;}return true;}}return false;}bool eraseR(const K& key){return _eraseR(_root, key);}//3.查找bool find(const K& key){Node* root = _root;while (root){if (root->_key < key){root = root->_right;}else if (root->_key > key){root = root->_left;}else{return true;}}return false;}bool findR(const K& key){return _findR(_root, key);}//4.打印void inOrder(){_inOrder(_root);cout << endl;}private://1.销毁(提供给析构)void destroy(Node*& root){if (root == nullptr)return;destroy(root->_left);destroy(root->_right);delete root;root = nullptr;}//2.拷贝(提供给拷贝构造)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;}//3.插入(提供给递归插入)bool _insertR(Node*& root, const K& key)//注意root是引用{if (root == nullptr){root = new Node(key);//这里由于传递的是引用,那么root就是上一级递归的root->_left或者root->_rightreturn true;}if (root->_key < key){return _insertR(root->_right, key);}else if (root->_key > key){return _insertR(root->_left, key);}else{return false;}}//4.删除(提供给递归插入)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//root->_key == key{Node* del = root;//保存要删除的节点if (root->_right == nullptr){root = root->_left;}else if (root->_left == nullptr){root = root->_right;}else//左右均不为空{Node* maxleft = root->_left;while (maxleft->_right != nullptr)//找左树的最大节点{maxleft = maxleft->_right;}swap(root->_key, maxleft->_key);return _eraseR(root->_left, key);//由于左树的最大节点必有一个空孩子节点,因此使用递归删除即可,可以看到递归的删除比非递归及其的简单明了(注意不可以直接传递maxleft,这是一个局部变量)}delete del;return true;}}//5.查找(提供给递归查找)bool _findR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key == key)return true;if (root->_key < key){return _isRecursionFind(root->_left, key);}else//root->_key > key{return _isRecursionFind(root->_right, key);}}//6.打印(提供给递归打印)void _inOrder(Node* root)//注意这里不能直接就拿_root当作缺省值了,因为缺省值只能是常量或者全局变量,而_root需要使用this->_root,而this指针是函数形参,不一定传过来了,别谈使用_root了{if (root == nullptr)return;_inOrder(root->_left);cout << root->_key << " ";_inOrder(root->_right);}//?.成员变量Node* _root;
};

这里我还为您提供了三个测试样例:

//普通测试
void test_1()
{BinarySearchTree<int> b;b.insert(6);b.insert(2);b.insert(1);b.insert(4);b.insert(-2);b.insert(10);b.insert(9);b.insert(11);b.inOrder();b.erase(6);b.inOrder();b.erase(2);b.inOrder();b.erase(10);b.inOrder();b.erase(1);b.inOrder();b.erase(4);b.inOrder();b.erase(9);b.inOrder();b.erase(11);b.inOrder();b.erase(-2);b.inOrder();
}
//头删测试(需要该_root为公有成员才可以测试)
void test_2()
{BinarySearchTree<int> b;b.insert(6);b.insert(2);b.insert(1);b.insert(4);b.insert(-2);b.insert(10);b.insert(9);b.insert(11);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();//b.erase(b._root->_key);//b.inOrder();
}
//递归测试
void test_3()
{BinarySearchTree<int> b;b.insertR(6);b.insertR(2);b.insertR(1);b.insertR(4);b.insertR(-2);b.insertR(10);b.insertR(9);b.insertR(11);BinarySearchTree<int> b1(b);b.inOrder();b.eraseR(6);b.inOrder();b.eraseR(2);b.inOrder();b.eraseR(10);b.inOrder();b.eraseR(1);b.inOrder();b.eraseR(4);b.inOrder();b.eraseR(9);b.inOrder();b.eraseR(11);b.inOrder();b.eraseR(-2);b.inOrder();b1.inOrder();b.inOrder();
}

3.二叉搜索树应用

3.1.Key模型

考虑“在不在”的问题,例如:门禁系统、车库系统、 单词检查…查找对象是否在数据库中存在?这些场景在现实中有很多。

3.2.Key/Value模型

通过一个值查找另外一个值,例如:中英文互译、电话号码查询快递信息、验证码查询信息…只需要在一个节点中包含一个数据对即可。另外我们之前说过二叉搜索树一般不存储重复的元素,如果相同的元素可以让该元素绑定一个int元素形成键值对,这种情况的实际应用有:统计高频词汇。

补充:实际上,上面的这两种模型对标的是C++setmap容器。

4.二叉搜索树分析

由于缺失平衡性,二叉搜索树在最不理想的状态查找的时间复杂度是O(n)

相关文章:

C++搜索二叉树

本章主要是二叉树的进阶部分&#xff0c;学习搜索二叉树可以更好理解后面的map和set的特性。 1.二叉搜索树概念 二叉搜索树的递归定义为&#xff1a;非空左子树所有元素都小于根节点的值&#xff0c;非空右子树所有元素都大于根节点的值&#xff0c;而左右子树也是二叉搜索树…...

软件工程17-18期末试卷

2.敏捷开发提倡一个迭代80%以上的时间都在编程&#xff0c;几乎没有设计阶段。敏捷方法可以说是一种无计划性和纪律性的方法。错 敏捷开发是一种软件开发方法论&#xff0c;它强调快速响应变化、持续交付有价值的软件、紧密合作和适应性。虽然敏捷方法鼓励迭代开发和灵活性&…...

课题学习(九)----阅读《导向钻井工具姿态动态测量的自适应滤波方法》论文笔记

一、 引言 引言直接从原论文复制&#xff0c;大概看一下论文的关键点&#xff1a; 垂直导向钻井工具在近钻头振动和工具旋转的钻井工作状态下&#xff0c;工具姿态参数的动态测量精度不高。为此&#xff0c;通过理论分析和数值仿真&#xff0c;提出了转速补偿的算法以消除工具旋…...

阿里云服务器—ECS快速入门

这里对标阿里云的课程&#xff0c;一步步学习&#xff0c;链接在下面&#xff0c;学习完考试及格即可获取阿里云开发认证和领取证书&#xff0c;大家可以看看这个&#xff0c;这里我当作笔记&#xff0c;记一下提升印象&#xff01; 内容很长&#xff0c;请耐心看完&#xff0…...

Hive简介及核心概念

本专栏案例数据集链接: https://download.csdn.net/download/shangjg03/88478038 1.简介 Hive 是一个构建在 Hadoop 之上的数据仓库,它可以将结构化的数据文件映射成表,并提供类 SQL 查询功能,用于查询的 SQL 语句会被转化为 MapReduce 作业,然后提交到 Hadoop 上运行。 …...

CrossOver 23.6.0 虚拟机新功能介绍

CrossOver 23.6.0 Mac 此应用程序允许您运行为 Microsoft Windows 编写的程序&#xff0c;而无需实际安装操作系统。 CrossOver 23.6.0 Mac 包括一个 Windows 程序库&#xff0c;用于它可以运行的 Windows 程序。 您会发现非常流行的应用程序&#xff0c;例如 Microsoft Word…...

(免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设农产品销售管理系统。…...

centos更改yum源

1、更改yum源 阿里云/etc/yum.repos.d/CentOS-Base.repo 金山云/etc/yum.repos.d/cloud.repo vi /etc/yum.repos.d/cloud.repo 替换为 [base] nameCentOS-$releasever - Base mirrorlisthttp://mirrorlist.centos.org/?release$releasever&arch$basearch&repoos&…...

React-快速搭建开发环境

1.安装 说明&#xff1a;react-excise-01是创建的文件名 npx create-react-app react-excise-01 2. 打开文件 说明:we suggest that you begin by typing:下面即是步骤。 cd react-excise-01 npm start 3.显示...

算法随想录算法训练营第四十六天| 583. 两个字符串的删除操作 72. 编辑距离

583. 两个字符串的删除操作 题目&#xff1a;给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 思路&#xff1a;这题思路主要是求出 word1 字符串和 word2 字符串中的最长相同的子字符串&…...

vue源码分析(五)——vue render 函数的使用

文章目录 前言一、render函数1、render函数是什么&#xff1f; 二、render 源码分析1.执行initRender方法2.vm._c 和 vm.$createElement 调用 createElement 方法详解&#xff08;1&#xff09;区别&#xff08;2&#xff09;代码 3、原型上的_render方法&#xff08;1&#xf…...

Maven第三章:IDEA集成与常见问题

Maven第三章:IDEA集成与常见问题 前言 本章内容重点:了解如何将Maven集成到IDE(如IntelliJ IDEA或Eclipse)中,以及使用过程中遇到的常见的问题、如何解决,如何避免等,可以大大提高开发效率。 IEAD导入Maven项目 File ->Open 选择上一章创建的Maven项目 my-app查看po…...

数据结构—线性实习题目(二)5迷宫问题(栈)

迷宫问题&#xff08;栈&#xff09; #include <iostream>​ #include <assert.h> using namespace std;int qi1, qi2; int n; int m1, p1; int** Maze NULL; int** mark NULL;struct items {int x, y, dir; };struct offsets {int a, b;char* dir; };const int…...

Nginx 的配置文件(负载均衡,反向代理)

Nginx可以配置代理多台服务器&#xff0c;当一台服务器宕机之后&#xff0c;仍能保持系统可用。 cmd查找端口是否使用&#xff1a;netstat -ano Nginx出现403 forbidden #解决办法&#xff1a;修改web目录的读写权限&#xff0c;或者是把nginx的启动用户改成目录的所属用户&…...

项目管理49个过程定义与作用、五大过程组

五大过程组&#xff1a; 49个过程的定义与作用&#xff1a; 1.整合管理&#xff1a; (1)制定项目章程&#xff1a;制定项目章程是编写一份正式批准项目并授予项目经理权力的文件的过程&#xff0c;其作用是①确立组织与项目的关系&#xff1b;②展示组织对项目的承诺&#xff…...

MySQL篇---第六篇

系列文章目录 文章目录 系列文章目录一、 MySQL 中 varchar 与 char 的区别?varchar(30) 中的 30代表的涵义?二、 int(11) 中的 11 代表什么涵义?三、为什么 SELECT COUNT(*) FROM table 在 InnoDB 比MyISAM 慢?一、 MySQL 中 varchar 与 char 的区别?varchar(30) 中的 30…...

QA新人入职任务

一、背景 分享记录一下入职新公司后&#xff0c;新人第一周接到的新手任务&#xff0c;回顾总结&#xff0c;方便自己成长和思考~ 二、新人任务说明 题目1&#xff1a;接口相关 题目2&#xff1a;UI相关 UI原型图 三、任务要求 1、根据题目2原型图&#xff0c;进行UI测试…...

更新电脑显卡驱动的操作方法有哪些?

更新显卡驱动可以有效的提升我们电脑的性能&#xff0c;可以通过设备管理器、显卡驱动软件等方式进行检查驱动是否需要更新&#xff0c;并修复一些电脑上已知的显卡问题。 然而&#xff0c;对于一些不是很懂电脑技术的人员来说&#xff0c;更新电脑显卡驱动是一件比较复杂和混乱…...

[Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷

一.Docker 部署 Nginx 以及端口映射 Docker 部署 Nginx,首先需要下载nginx镜像,然后启动这个镜像,就运行了一个nginx的容器了 1.下载 nginx 镜像并启动容器 #查看是否存在nginx镜像:发现没有nginx镜像 [rootlocalhost zph]# docker images | grep nginx#下载nginx镜像 [rootl…...

【0基础学Java第三课】-- 运算符

3. 运算符 3.1 什么是运算符3.2 算术运算符3.2.1 **基本四则运算符&#xff1a;加减乘除模( - * / %&#xff09;**3.2.2 增量运算符 - * %3.2.3 自增/自减运算符 -- 3.3 关系运算符3.4逻辑运算符(重点)3.4.1 逻辑与 &&3.4.2 逻辑 ||3.4.3逻辑非 !3.4.4 短路求值 3.5 …...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...

职坐标物联网全栈开发全流程解析

物联网全栈开发涵盖从物理设备到上层应用的完整技术链路&#xff0c;其核心流程可归纳为四大模块&#xff1a;感知层数据采集、网络层协议交互、平台层资源管理及应用层功能实现。每个模块的技术选型与实现方式直接影响系统性能与扩展性&#xff0c;例如传感器选型需平衡精度与…...

自定义线程池1.2

自定义线程池 1.2 1. 简介 上次我们实现了 1.1 版本&#xff0c;将线程池中的线程数量交给使用者决定&#xff0c;并且将线程的创建延迟到任务提交的时候&#xff0c;在本文中我们将对这个版本进行如下的优化&#xff1a; 在新建线程时交给线程一个任务。让线程在某种情况下…...