【C++高阶一】二叉搜索树

【C++高阶一】二叉搜索树剖析

  • 1.什么是二叉搜索树
  • 2.二叉搜索树非递归实现
    • 2.1插入
    • 2.2删除
      • 2.2.1删除分析一
      • 2.2.2删除分析二
    • 2.3查找
  • 3.二叉搜索树递归实现
    • 3.1插入
    • 3.2删除
    • 3.3查找
  • 4.完整代码

1.什么是二叉搜索树

任何一个节点,他的左子树的所有节点都比他小,右子树的所有节点都比他大

在这里插入图片描述

二叉搜索树是有序的,中序遍历这棵二叉搜索树刚好是升序
时间复杂度为O(N),因为会出现这种极端情况
在这里插入图片描述
二叉搜索树中不能出现值相同的节点

2.二叉搜索树非递归实现

大致框架

template<class K>
struct BSTreeNode //二叉搜索树节点
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree//二叉搜索树
{typedef BSTreeNode<K> Node;
public:private:Node* _root = nullptr;
};

2.1插入

插入的值比根大就往右走,比根小就往左走,如果遇见和插入值相同的值就返回false

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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else {parent->_right = cur;}return true;}

parent保存每一步的父节点,在插入之后起到父子节点连接作用

2.2删除

2.2.1删除分析一

  1. 被删除节点无孩子:
    直接将此节点删除,不用做特殊处理
  2. 被删除节点只有左孩子:
    此节点被删除后,将此节点的左孩子连接到此节点的父亲节点
  3. 被删除节点只有右孩子:
    此节点被删除后,将此节点的右孩子连接到此节点的父亲节点
bool erase(const k& 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 (cur == parent->_right)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;}else if (cur->_right == nullptr)//右孩子为空{if (cur == _root)_root = cur->_left;else{if (cur == parent->_right)parent->_right = cur->_leftelseparent->_left = cur->_left;}delete cur;}//第四种情况 //else{}}}return false}

表面只写了两种情况,其实已经把无孩子的情况包括了

2.2.2删除分析二

当被删除的节点存在左右孩子时,我们要使用替换法
被替换的节点一定要满足一个条件:
是左子树的最大值 || 是右子树的最小值
在这里插入图片描述

假设使用右子树中最小的节点来替换,其左孩子为空,但右孩子未知,还需要分情况讨论

else//左右孩子都不为空
{//用右子树最小值来替换Node* Temp_parent = cur;//临时父节点Node* R_subtree_min = cur->_right;//右子树最小值while (R_subtree_min->_left){Temp_parent = R_subtree_min;R_subtree_min = R_subtree_min->_left;}swap(cur->_key, R_subtree_min->_key);if (R_subtree_min == Temp_parent->_left){Temp_parent->_left = R_subtree_min->_right;}else{Temp_parent->_right = R_subtree_min->_right;}delete R_subtree_min;
}

2.3查找

bool find(const K& 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;
}

3.二叉搜索树递归实现

递归实现全都使用了函数嵌套的方式,是为了使用_root这个定义在类中的成员变量

3.1插入

插入的基本思路一样,但递归到空时构建新节点的链接有三种方式:
1.多传一个参数,记录父节点
2.递归到空节点的上一层,比如if(root->_left==NULL) 开始构建节点
3.传引用
前两个方法和循环写法没什么区别,下面都使用传引用

bool Re_insert(const K& key)
{return _Re_insert(_root, key);
}bool _Re_insert(Node*& root, const K& key)
{if (root == nullptr){root = new Node(key);return true;}if (root->_key > key){return _Re_insert(root->_left, key);}else if (root->_key < key){return _Re_insert(root->_right, key);}else{return false;}
}

3.2删除

bool Re_erase(const K& key)
{return _Re_erase(_root, key);
}
bool _Re_erase(Node*& root, const K& key)
{if (root == nullptr)return false;if (root->_key > key){return _Re_erase(root->_left, key);}else if (root->_key < key){return _Re_erase(root->_right, key);}else//找到了,准备删除{if (root->_left == nullptr)//左孩子为空{Node* temp = root;root = root->_right;delete temp;return true;}else if (root->_right == nullptr)//右孩子为空{Node* temp = root;root = root->_left;delete temp;return true;}else//左右孩子都不为空{//找右子树的最小值Node* temp = root->_right;while (temp->_left){temp = temp->_left;}swap(root->_key, temp->_key);return _Re_erase(root->_right, key);}}
}

3.3查找

bool Re_find(const K& key)
{return _Re_find(_root, key);
}
private:
bool _Re_find(Node* root,const K& key)
{if (root == nullptr)return false;if (root->_key > kry){return _Re_find(root->_left, key);}else if (root->_key < kry){return _Re_find(root->_right, key);}else{return true;}
}

4.完整代码

template<class K>
struct BSTreeNode //二叉搜索树节点
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree//二叉搜索树
{typedef BSTreeNode<K> Node;
public: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->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else {parent->_right = cur;}return true;}bool erase(const K& 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 (cur == parent->_right)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;}else if (cur->_right == nullptr)//右孩子为空{if (cur == _root)_root = cur->_left;else{if (cur == parent->_right)parent->_right = cur->_leftelseparent->_left = cur->_left;}delete cur;}else//左右孩子都不为空{//用右子树最小值来替换Node* Temp_parent = cur;//临时父节点Node* R_subtree_min = cur->_right;//右子树最小值while (R_subtree_min->_left){Temp_parent = R_subtree_min;R_subtree_min = R_subtree_min->_left;}swap(cur->_key, R_subtree_min->_key);if (R_subtree_min == Temp_parent->_left){Temp_parent->_left = R_subtree_min->_right;}else{Temp_parent->_right = R_subtree_min->_right;}delete R_subtree_min;}return true;}}return false;}bool find(const K& 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 Re_insert(const K& key){return _Re_insert(_root, key);}bool Re_erase(const K& key){return _Re_erase(_root, key);}bool Re_find(const K& key){return _Re_find(_root, key);}
private:bool _Re_insert(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key > key){return _Re_insert(root->_left, key);}else if (root->_key < key){return _Re_insert(root->_right, key);}else{return false;}}bool _Re_erase(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key > key){return _Re_erase(root->_left, key);}else if (root->_key < key){return _Re_erase(root->_right, key);}else//找到了,准备删除{if (root->_left == nullptr)//左孩子为空{Node* temp = root;root = root->_right;delete temp;return true;}else if (root->_right == nullptr)//右孩子为空{Node* temp = root;root = root->_left;delete temp;return true;}else//左右孩子都不为空{//找右子树的最小值Node* temp = root->_right;while (temp->_left){temp = temp->_left;}swap(root->_key, temp->_key);return _Re_erase(root->_right, key);}}}bool _Re_find(Node* root,const K& key){if (root == nullptr)return false;if (root->_key > kry){return _Re_find(root->_left, key);}else if (root->_key < kry){return _Re_find(root->_right, key);}else{return true;}}
private:Node* _root = nullptr;
};

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

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

相关文章

前端面试热门知识点总结

URL从输入到页面展示的过程 版本1 1.用户在浏览器的地址栏输入访问的URL地址。浏览器会先根据这个URL查看浏览器缓存-系统缓存-路由器缓存&#xff0c;若缓存中有&#xff0c;直接跳到第6步操作&#xff0c;若没有&#xff0c;则按照下面的步骤进行操作。 2.浏览器根据输入的UR…

Swagger | 解决Springboot2.x/3.x不兼容和依赖报错等问题

目录 不兼容报错提醒 1. 修改Spring Boot版本 2. 修改application.yml配置文件 3. 使用其他替代方案 依赖兼容 配置 Yaml 文件 依赖报错提醒 解决方法 1. 选择一个库 2. 移除springfox依赖 3. 添加springdoc依赖 4. 配置springdoc 5. 清理项目 6. 启动项目 示例代…

C++默认构造函数、普通构造函数、拷贝构造、移动构造、委托构造及析构函数深度解析

目录 一、默认构造函数&#xff08;Default Constructor&#xff09;二、普通构造函数&#xff08;General Constructor&#xff09;三、拷贝构造函数&#xff08;Copy Constructor&#xff09;四、移动构造函数&#xff08;Move Constructor&#xff0c;C11&#xff09;五、委…

JVM 深度解析

一、JVM 概述 1.1 什么是 JVM&#xff1f; JVM&#xff08;Java Virtual Machine&#xff0c;Java 虚拟机&#xff09;是 Java 程序运行的核心引擎。它像一个“翻译官”&#xff0c;将 Java 字节码转换为机器能理解的指令&#xff0c;并管理程序运行时的内存、线程等资源。 …

OpenCV CUDA 模块图像过滤-----创建一个计算图像导数的滤波器函数createDerivFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::createDerivFilter 是 OpenCV CUDA 模块中的一个工厂函数&#xff0c;用于创建一个计算图像导数的滤波器。这个滤波器可以用来计算图像…

Spring Boot 接口开发实战指南

Spring Boot 接口开发实战指南 一、基础接口开发步骤 1.1 添加必要依赖 <!-- pom.xml --> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></depen…

题目 3325: 蓝桥杯2025年第十六届省赛真题-2025 图形

题目 3325: 蓝桥杯2025年第十六届省赛真题-2025 图形 时间限制: 2s 内存限制: 192MB 提交: 494 解决: 206 题目描述 小蓝要画一个 2025 图形。图形的形状为一个 h w 的矩形&#xff0c;其中 h 表示图形的高&#xff0c;w 表示图形的宽。当 h 5,w 10 时&#xff0c;图形如下所…

UML 时序图 使用案例

UML 时序图 UML 时序图 (Sequence Diagram)时序图的主要元素消息类型详解时序图示例时序图绘制步骤时序图的应用场景 UML 时序图 (Sequence Diagram) 时序图是UML(统一建模语言)中用于展示对象之间交互行为的动态视图&#xff0c;它特别强调消息的时间顺序。 时序图的主要元素…

PPT连同备注页(演讲者模式)一块转为PDF

首先&#xff0c;进入创建PDF/XPS&#xff1a; 然后进入选项&#xff1a; 发布选项-发布内容里选备注页&#xff1a; 导出的原始结果是这样的&#xff1a; 这个时候裁剪一下&#xff0c;范围为所有页面&#xff1a; 最终结果&#xff1a; 如果导出不选“备注页”而是只勾选“包…

AI时代新词-多模态(Multimodal)

一、什么是多模态&#xff08;Multimodal&#xff09;&#xff1f; 多模态&#xff08;Multimodal&#xff09;是指在人工智能中&#xff0c;融合多种不同类型的信息&#xff08;如文本、图像、语音、视频等&#xff09;进行处理和分析的技术。与传统的单一模态&#xff08;例…

【图像大模型】Stable Diffusion XL:下一代文本到图像生成模型的技术突破与实践指南

Stable Diffusion XL&#xff1a;下一代文本到图像生成模型的技术突破与实践指南 一、架构设计与技术演进1.1 核心架构革新1.2 关键技术突破1.2.1 双文本编码器融合1.2.2 动态扩散调度 二、系统架构解析2.1 完整生成流程2.2 性能指标对比 三、实战部署指南3.1 环境配置3.2 基础…

图像分割技术的实现与比较分析

引言 图像分割是计算机视觉领域中的一项基础技术&#xff0c;其目标是将数字图像划分为多个图像子区域&#xff08;像素的集合&#xff09;&#xff0c;以简化图像表示&#xff0c;便于后续分析和理解。在医学影像、遥感图像分析、自动驾驶、工业检测等众多领域&#xff0c;图…

摩尔线程S4000国产信创计算卡性能实战——Pytorch转译,多卡P2P通信与MUSA编程

简介 MTT S4000 是基于摩尔线程曲院 GPU 架构打造的全功能元计算卡&#xff0c;为千亿规模大语言模型的训练、微调和推理进行了定制优化&#xff0c;结合先进的图形渲染能力、视频编解码能力和超高清 8K HDR 显示能力&#xff0c;助力人工智能、图形渲染、多媒体、科学计算与物…

「从0到1」构建工业物联网监控系统:ARM+Quarkus+Prometheus技术栈全记录

在工业4.0浪潮中&#xff0c;边缘计算正成为智能制造的核心基础设施。ARM架构边缘计算机凭借其低功耗、高能效比和模块化设计优势&#xff0c;正在重塑工业物联网&#xff08;IIoT&#xff09;的监控体系。当Java的跨平台能力与Prometheus的实时监控体系相结合&#xff0c;为工…

【HW系列】—web常规漏洞(文件上传漏洞)

文章目录 一、简介二、危害三、文件检测方式分类四、判断文件检测方式五、文件上传绕过技术六、漏洞防御措施 一、简介 文件上传漏洞是指Web应用程序在处理用户上传文件时&#xff0c;未对文件类型、内容、路径等进行严格校验和限制&#xff0c;导致攻击者可上传恶意文件&…

如何设计ES的冷热数据分离架构?Elasticsearch 集群如何实现高可用?如何避免脑裂问题?如果出现脑裂如何恢复?

以下为Elasticsearch架构设计与高可用方案详细说明&#xff1a; 冷热架构 一、冷热数据分离架构设计&#xff08;文字描述模拟架构图&#xff09; [Hot Layer] │ ├─ SSD节点组&#xff08;3节点&#xff09; │ ├─ 角色&#xff1a;ingest/data/hot │ ├─ 存…

Trivy 镜像漏洞扫描:从零入门到实战指南

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 ——手把手带你掌握容器安全核心工具 一、安装配置&#xff1a;三步完成 Trivy 部署 Trivy 是由 Aqua Security 开发的开源容器安全工具&#xff0c;支持…

SQL基础概念以及SQL的执行方式

1. SQL入门 1.1. SQL语言功能 可以把 SQL 语言按照功能划分成以下的 4 个部分&#xff1a; DDL&#xff0c;英文叫做 Data Definition Language&#xff0c;也就是数据定义语言&#xff0c;它用来定义我们的数据库对象&#xff0c;包括数据库、数据表和列。通过使用 DDL&…

Rust 1.0 发布十周年,梦想再度扬帆起航!

目录 引言&#xff1a;发布十周年&#xff0c;锋芒露今朝 一、Rust的诞生&#xff1a;源于安全的初心 二、Rust 1.0&#xff1a;十年耕耘&#xff0c;硕果累累 三、核心利器&#xff1a;安全、并发与性能的十年锤炼 四、生态与应用&#xff1a;十年拓展&#xff0c;遍地开…

x86 与 ARM 汇编深度对比:聚焦 x86 汇编的独特魅力

一、引言 汇编语言是硬件与软件的桥梁&#xff0c;x86 和 ARM 作为两大主流架构&#xff0c;其汇编语言在设计理念、指令集、编程风格上差异显著。本文以 x86 汇编为核心&#xff0c;结合与 ARM 的对比&#xff0c;解析 x86 汇编的技术细节与应用场景&#xff0c;助力开发者深…