《C++二叉搜索树原理剖析:从原理到高效实现教学》

《C++二叉搜索树原理剖析:从原理到高效实现教学》
前引:二叉搜索树(Binary Search Tree, BST)作为一种基础且强大的数据结构,凭借其高效的查找与插入效率,成为算法设计与内存优化的核心工具。在C++中,BST不仅能实现高效的数据管理,更为平衡树(如AVL树)奠定理论基础。本文将深入剖析BST的有序性本质(结合C++特性详解插入、删除、遍历等关键操作,并提供内存安全的现代C++实现范式!

目录

【一】二叉搜索树介绍

【二】特点剖析

【三】二叉搜索树实现

(1)结构创建

(2)插入节点

(3)中序遍历

(4)查找节点

(5)删除节点

(6)析构

(7)拷贝构造


【一】二叉搜索树介绍

二叉搜索树又称二叉排序树,我们根据它的名字猜到是一颗二叉树完成了排序的工作?二叉树如何排序?下面我们来看看它和我们之前学习的大小顶堆有和区别!

【二】特点剖析

例如下面一棵二叉搜索树(可以为空树):

二叉搜索树语言描绘特征如下:

(1)从第一个父节点(根节点)开始,它的左子节点小于父节点

(2)从第一个父节点(根节点)开始,它的右子节点大于父节点

(3)它的左右子树也分别为二叉搜索树

【三】二叉搜索树实现

(1)结构创建

实现一棵二叉搜索树,我们需要一个节点结构、一个功能结构

节点结构里面有左右子节点(left,right)、一个数据存储变量(date):

注意:主模板的声明不允许使用模板参数

功能结构用来实现二叉搜索树的功能:
(2)插入节点

插入节点我们需要根据数据的大小来判断插在左右节点的 nullptr 位置,我们这里挑战循环来写

注意:我们需要用其它节点代替node去移动,不然node每次都不是指向根节点的

//插入节点 void Insert(const T& date) { //如果根节点为空 if (node == nullptr) { node = new Node(date); return; } //根据数据大小查找 Node* parent = nullptr; Node* cur = node; while (cur) { //记录父节点 parent = cur; //如果小于父节点 if (date < cur->date) { cur = cur->left; } else { cur = cur->right; } } //此时已经到了节点为空的位置 //如果小于父节点 if (date < parent->date) { //插在父节点左侧 parent->left = new Node(date); return; } else { //插在父节点右侧 parent->right = new Node(date); return; } }
(3)中序遍历

中序我们调用递归来完成:先遍历左子树,然后父节点,然后右子树

//中序遍历 void Inorder() { _Inorder(node); } void _Inorder(Node* ptr) { //遇到空就返回 if (ptr == nullptr) { return; } _Inorder(ptr->left); cout << ptr->date << " "; _Inorder(ptr->right); }
效果展示:

(4)查找节点

找到对应节点之后,然后返回即可:

根据要找的数据大小去查找,那么最多查找次数就是二叉树的深度次

//查找数据 bool Find(const T& date) { //如果为空树,返回 if (node == nullptr) { return false; } Node* cur = node; //找节点 while (cur) { //如果date大于父节点,右边找,否则左边找 if (date > cur->date) { cur = cur->right; } else if(date < cur->date) { cur = cur->left; } else { return true; } } //如果出循环了还没有返回就说明没有找到 return false; }
效果展示:

(5)删除节点

删除节点我们需要考虑下面这三个情况(重点是比较节点数据大小):

(1)该节点无孩子节点:先删,然后置空

(2)该节点有一个孩子节点:先连接再删

注意:这两种情况可以概括为一类,参考下面代码注释,比较简单我们就直接看代码
(3)该节点有两个孩子节点:我们需要找一定大小的节点去替代它

替代思路:让它的左子树最大值或者右子树最小值去替换,然后删除它(左子树max为例)

·

解释:例如下面这幅图,我们要删除3



(1)先找到目标节点cur(3),然后找目标节点左子树的最大值left_max

(2)交换目标节点cur和最大值 light_max的数据

(3)这里需要标记 lleft_max的父节点为 parent

第一种情况:

(4)因为找的是左子树的最大值,所以只可能父节点parent的右边还存在子节点

         将它连接在parent的右边

(5)再将cur指向 left_max,删除

第二种情况:



(4)因为找的是左子树的最大值,可能 parent 的左边还存在子节点

(5)再将cur指向 left_max,删除


效果展示:

(6)析构

我们可以利用上面的“删除节点”+“根节点是否为空循环”来不断析构

注意:上面我们的析构是利用第三方指针cur代替node删除的,所以当二叉树只有一个根节点删除             后需要考虑置空,这样才可以利用到循环

//析构 ~BST() { while (node) { Erase(node->date); cout << "删除成功" << endl; } }

测试:

(7)拷贝构造

拷贝构造我们可以利用递归遍历不断开新节点

注意:递归左右节点时要连接起来,下面有详细的批注

//拷贝构造 BST(const BST<T>& ptr) { //如果拷贝对象是空 if (ptr.node == nullptr) { return; } //这里的ptr是拷贝的对象,node是待拷贝的对象的根节点 node = Copy(node, ptr.node); } Node* Copy(Node* _node,Node* copy_node) { if (copy_node == nullptr) { return nullptr; } //前序拷贝 _node = new Node(copy_node->date); // 空间是开辟成功了,但是这里node的左右子树,没有连接,需要接收copy的返回值才能完成连接 _node->left = Copy(_node->left, copy_node->left); _node->right = Copy(_node->right, copy_node->right); //返回根节点 return _node; }

效果展示:

Read more

15.8【保姆级教程】C语言链式结构(链表):从0到1吃透单链表/双向链表,手写完整实战案例

15.8【保姆级教程】C语言链式结构(链表):从0到1吃透单链表/双向链表,手写完整实战案例

🔥 关注博主不迷路!纯干货+可视化图解+完整可运行代码 | 解决数组痛点,掌握队列/二叉树的核心基础 在C语言中,链式结构(链表) 是突破数组“固定长度、插入删除低效”痛点的核心数据结构,也是实现队列、栈、二叉树、哈希表等高级数据结构的基础。不同于数组的连续内存布局,链式结构通过“指针”将分散的内存节点串联起来,实现数据的动态增删,是C语言进阶必须吃透的核心知识点。 本文将从“链式结构的核心认知”到“单链表完整实现”,再到“双向链表进阶”和“实战案例”,手把手拆解每一行代码、每一个逻辑,即使是零基础新手,也能轻松掌握链式结构的所有核心用法! 一、链式结构核心认知:为什么需要它? 1.1 数组的痛点(链式结构的诞生背景) 在学习链表前,先明确“为什么不用数组”——数组的三大致命缺陷: 数组的痛点具体问题长度固定声明时必须指定长度(如int arr[

By Ne0inhk
五大经典排序算法:插入、希尔、冒泡、选择、堆排序全攻略

五大经典排序算法:插入、希尔、冒泡、选择、堆排序全攻略

目录 --------------插入排序------------- 1、插入排序思想 2、示例代码 3、效率分析 --------------希尔排序------------- 1、希尔排序思想 2、示例代码 3、效率分析 --------------选择排序------------- 1、选择排序思想 2、示例代码 3、效率分析 ---------------堆排序-------------- 1、堆排序思想 2、示例代码 3、效率分析 --------------冒泡排序------------- 1、冒泡排序思想 2、示例代码 3、效率分析 上述五大排序性能对比: --------------插入排序------------- 1、插入排序思想 插入排序的核心思想是逐步构建有序序列: 将数组分为 “已排序” 和 “未排序” 两部分,初始时已排序部分只包含第一个元素。 每次从未排序部分取出第一个元素,将其向前插入到已排序序列中的正确位置,使得插入后的序列依然保持有序。

By Ne0inhk
【数据结构手札】顺序表实战指南(二):结构体构建 | 初始化 | 打印 | 销毁

【数据结构手札】顺序表实战指南(二):结构体构建 | 初始化 | 打印 | 销毁

🌈个人主页:聆风吟 🔥系列专栏:数据结构手札 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 * 📚专栏订阅推荐 * 📋前言 - 顺序表文章合集 * 一. ⛳️顺序表:重点回顾 * 1.1 🔔顺序表的定义 * 1.2 🔔顺序表的分类 * 1.2.1 👻静态顺序表 * 1.2.2 👻动态顺序表 * 二. ⛳️顺序表的基本操作实现 * 2.1 🔔动态顺序表结构体构建 * 2.2 🔔初始化顺序表 * 2.3 🔔销毁顺序表 * 2.4 🔔打印顺序表 * 三. ⛳️顺序表的源代码 * 3.1 🔔SeqList.h 顺序表的函数声明 * 3.

By Ne0inhk
Python从0到100(九十二):Swin Transformer 架构解析及在UCI-HAR行为识别中的实现

Python从0到100(九十二):Swin Transformer 架构解析及在UCI-HAR行为识别中的实现

前言:零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学业升学和工作就业的先行者! 【优惠信息】 • 新专栏订阅前200名享9.9元优惠 • 订阅量破200后价格上涨至19.9元 • 订阅本专栏可免费加入粉丝福利群,享受: - 所有问题解答 -专属福利领取 欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程! 本文目录: * 一. Swin Transformer的基础原理 * 1. Transformer在视觉任务中的挑战 * 2. Swin Transformer的核心思想 * 二、 Swin Transformer的架构 * 1. Patch Embedding * 2. 位置编码 * 3. Swin Transformer Block * 4. 窗

By Ne0inhk