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

C++之二叉搜索树及其实现

一.二叉搜索树的基本概念

二叉搜索树(Binary Search Tree,简称 BST)是一种特殊的二叉树,它满足以下性质: 对于树中的每个节点,其左子树中所有节点的值都小于该节点的值 对于树中的每个节点,其右子树中所有节点的值都大于该节点的值 左右子树也分别是二叉搜索树 这种特性使得二叉搜索树在查找、插入和删除操作上具有较高的效率,平均时间复杂度为 O (log n)。

在这里插入图片描述

二.二叉搜索树的性能分析

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为:log2 N 最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为:N 所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为:O(N)

⼆分查找也可以实现O(log2N) 级别的查找效率,但是⼆分查找有两⼤缺陷:

  1. 需要存储在⽀持下标随机访问的结构中,并且有序。
  2. 插⼊和删除数据效率很低,因为存储在下标随机访问的结构中,插⼊和删除数据⼀般需要挪动数据。

这⾥也就体现出了平衡⼆叉搜索树的价值。

三.二叉搜索树的实现

搜索二叉树的基本概念与特性

搜索二叉树是一种特殊的二叉树,它满足以下性质:

  • 若任意节点的左子树不为空,则左子树上所有节点的值均小于该节点的值
  • 若任意节点的右子树不为空,则右子树上所有节点的值均大于该节点的值
  • 任意节点的左、右子树也分别为搜索二叉树
  • 没有键值相等的节点

这些性质使得搜索二叉树在进行查找操作时具有天然的优势,我们可以利用节点值的大小关系快速缩小查找范围,类似于有序数组的二分查找。

搜索二叉树的节点结构设计

首先来看搜索二叉树的节点结构实现:

代码语言:javascript

AI代码解释

template<class K> struct BSTreeNode { K _key; BSTNode<K>* _left; BSTNode<K>* _right; BSTNode(const K& key) :_key(key) , _left(nullptr) , _right(nullptr) { } };
  • _key:节点的关键字,用于比较和查找
  • _left:指向左子节点的指针
  • _right:指向右子节点的指针
  • 构造函数:初始化节点的关键字,并将左右子节点指针置为nullptr

节点采用模板设计,使得该结构可以存储任意类型的数据,只要该类型支持比较运算。

搜索二叉树的类结构与核心操作
类的基本结构

代码语言:javascript

AI代码解释

template<class K> class BSTree { typedef BSTreeNode<K> Node; public: // 构造、拷贝构造、赋值运算符、析构函数 BSTree() = default; BSTree(const BSTree<K>& t); BSTree<K>& operator=(BSTree<K> t); ~BSTree(); // 核心操作 bool Insert(const K& key); bool Find(const K& key); bool Erase(const K& key); // 中序遍历 void InOrder(); private: // 中序遍历的递归辅助函数 void _InOrder(Node* root); // 拷贝构造的递归辅助函数 Node* Copy(Node* root); // 析构函数的递归辅助函数 void Destory(Node* root); private: Node* _root = nullptr; };
构造与析构函数
  • 默认构造函数:使用C++11的default关键字,生成默认的构造函数,将根节点初始化为nullptr
  • 拷贝构造函数:通过递归调用Copy函数实现深拷贝,确保新对象与原对象相互独立
  • 赋值运算符:采用"拷贝交换"技术,先创建传入对象的副本,然后交换当前对象与副本的根节点指针,保证异常安全性
  • 析构函数:调用Destory函数递归释放所有节点的内存,防止内存泄漏
插入操作(Insert)

插入操作是构建搜索二叉树的基础,其实现逻辑如下:

代码语言:javascript

AI代码解释

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->_left = cur; } else { parent->_right = cur; } return true; }

插入操作的步骤:

  1. 如果树为空,直接创建根节点
  2. 否则从根节点开始,比较当前节点值与插入值的大小:
    • 若当前节点值小于插入值,向右转
    • 若当前节点值大于插入值,向左转
    • 若相等,说明键值已存在,插入失败
  3. 找到合适的插入位置(空指针处)后,创建新节点并连接到父节点
查找操作(Find)

查找是搜索二叉树的核心功能,利用树的特性可以高效地定位目标节点:

代码语言:javascript

AI代码解释

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; // 未找到目标节点 }

查找操作的逻辑非常直观,与插入操作类似:

  • 从根节点开始,比较当前节点值与目标值
  • 根据大小关系决定向左还是向右查找
  • 若找到相等的值,返回true
  • 若遍历完所有可能的节点仍未找到,返回false
删除操作(Erase)

删除操作是搜索二叉树中最复杂的操作,需要考虑多种情况:

代码语言:javascript

AI代码解释

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) { // 情况1:左子树为空 if (cur == _root) { _root = _root->_right; } if (parent->_right == cur) { parent->_right = cur->_right; } else { parent->_left = cur->_right; } delete cur; } else if(cur->_right == nullptr) { // 情况2:右子树为空 if (cur == _root) { _root = _root->_left; } if (parent->_right == cur) { parent->_right = cur->_left; } else { parent->_left = cur->_left; } delete cur; } else { // 情况3:左右子树都不为空 Node* pMinRight = cur; Node* minRight = cur->_right; while (minRight->_left) { pMinRight = minRight; minRight = minRight->_left; } // 找到右子树中的最小节点(最左节点) swap(minRight->_key, cur->_key); // 删除右子树中的最小节点 if (pMinRight->_left == minRight) { pMinRight->_left = minRight->_right; } else { pMinRight->_right = minRight->_right; } delete minRight; } return true; } } return false; // 未找到要删除的节点 }

删除操作需要处理三种情况:

  1. 左子树为空:直接用右子树替换当前节点
  2. 右子树为空:直接用左子树替换当前节点
  3. 左右子树都不为空
    • 找到当前节点右子树中的最小节点(最左节点)
    • 将该最小节点的值与当前节点的值交换
    • 删除原来的最小节点(此时该节点最多只有一个右子树)

这种处理方式确保了删除节点后,搜索二叉树的性质仍然保持。


http://www.jsqmd.com/news/492450/

相关文章:

  • PP-DocLayoutV3插件开发:为Unity编辑器集成文档解析功能
  • Android 13 实战:突破分区存储,精准定位与读取外置SD卡文件
  • Qwen3-14B量化模型教程:AWQ权重校准原理与vLLM内核优化机制解析
  • FaceRecon-3D在网络安全中的应用:生物特征活体检测系统
  • 鼠标性能测试新纪元:MouseTester开源工具深度应用指南
  • 丹青识画系统VMware虚拟机内部署测试:跨平台环境兼容性指南
  • 文墨共鸣辅助操作系统学习:复杂概念讲解与命令手册查询
  • 零样本学习在未知领域推理任务中的应用
  • MNE-Python | 开源生理信号分析利器(二):从EEG/MEG数据到机器学习特征工程
  • 解锁不间断内容:构建全自动直播捕获系统的完整指南
  • FlowSDF中转换数据集格式的脚本
  • ADS中村田电感模型导入实战:.mod与.s2p文件的应用对比与性能分析
  • Phi-3-vision-128k-instruct教学场景应用:学生作业图像题自动解答案例
  • Vue大屏适配神器V-Scale-Screen实战:从4K到1080P的无缝缩放方案
  • 重大升级!戳戳 Oracle巡检系统,现已支持DG与RAC集群
  • 一只比芝麻还小的蜂,大脑只有几百个神经元,却让现在的AI显得很笨重
  • BunnyScholar和嘎嘎降AI怎么选?实测对比给你答案
  • Golang开发的Hawkeye工具全解析:从安装到高级功能使用指南
  • Qwen3-14b_int4_awq Chainlit前端实操:上传文件、多轮对话、清除历史记录
  • 罗兰艺境GEO技术架构:基于DSS原则的认知基建工程体系 - 罗兰艺境GEO
  • 基于ESP32-S3与TMC2209的立创EDA 3D裸眼风扇广告机开源项目全解析
  • 3步解决ComfyUI-Florence2模型加载故障终极指南
  • AD组策略密码安全配置指南:从默认策略到企业级防护
  • 轻量模型新选择:Qwen1.5-1.8B GPTQ与同类模型在AIGC任务上的效果横评
  • 3/15打卡
  • ai辅助开发新体验:让快马ai智能推荐并验证win10镜像
  • 企业级渗透测试实战:如何用AppScan标准版快速定位SQL注入漏洞(附登录态配置技巧)
  • 存储型XSS的隐藏威胁:如何通过评论区漏洞入侵你的网站
  • 【Rust日报】 RAVEN — RISC-V 模拟器与集成开发环境
  • 告别重复造轮子:用快马ai编程一键生成用户认证模块提升效率