《大话数据结构》第8章精读:二叉排序树(BST)完整 C++ 实现(插入、查找、删除、遍历)
1. 二叉排序树的定义进入《大话数据结构》第8章「查找」本章第一个重点就是二叉排序树Binary Sort Tree / Binary Search Tree简称 BST。它把“排序”和“查找”结合在一起是后续平衡二叉树、B 树等内容的基础。二叉排序树可以是一棵空树如果不是空树它必须满足以下性质若左子树不为空则左子树上所有节点的值均小于根节点的值若右子树不为空则右子树上所有节点的值均大于根节点的值左右子树本身也都是二叉排序树。关键结论对二叉排序树进行中序遍历会得到一个递增的有序序列。这也是它能够把“查找”和“排序”结合起来的重要原因。2. 节点定义与基本结构下面使用 C 定义 BST 节点。每个节点保存一个整型关键字以及指向左右孩子的两个指针。#include iostream #include queue using namespace std; struct BSTNode { int data; // 节点关键字 BSTNode *lchild; // 左孩子指针 BSTNode *rchild; // 右孩子指针 BSTNode(int d) : data(d), lchild(nullptr), rchild(nullptr) {} };说明data当前节点保存的值。lchild左孩子指针指向一棵更小的二叉排序树。rchild右孩子指针指向一棵更大的二叉排序树。构造函数使用初始化列表一次性完成成员初始化避免指针成为未初始化的野指针。3. BSTree 类与递归插入插入操作按“比较、递归、挂接”的思路进行如果当前节点为空就新建节点如果插入值小于当前节点则递归插入左子树如果大于当前节点则递归插入右子树如果相等通常不重复插入也可以根据业务需求进行更新。class BSTree { private: BSTNode* root; // 递归插入 BSTNode* insert(BSTNode* node, int key) { if (node nullptr) { return new BSTNode(key); } if (key node-data) { node-lchild insert(node-lchild, key); } else if (key node-data) { node-rchild insert(node-rchild, key); } // 相等则不插入或根据实际需求更新节点 return node; }插入元素{50, 30, 70, 20, 40, 60, 80}后会形成如下结构50 / \ 30 70 / \ / \ 20 40 60 80可以看到根节点 50 的左子树全部小于 50右子树全部大于 50每一棵子树也满足同样的性质。4. 递归查找查找操作与二分查找思想类似每次比较当前节点与目标值如果相等则查找成功如果目标值小于当前节点则进入左子树继续查找否则进入右子树继续查找。走到空指针仍未找到说明树中不存在该值。// 递归查找 BSTNode* search(BSTNode* node, int key) { if (node nullptr || node-data key) { return node; } if (key node-data) { return search(node-lchild, key); } else { return search(node-rchild, key); } }查找路径总是沿着“小于走左大于走右”的方向向下延伸。树的形态越接近完全二叉树查找效率越接近O(log n)。5. 查找最小节点在一棵二叉排序树中沿左孩子不断前进最后一个非空节点就是当前子树中的最小节点。这个操作在删除有两个孩子的节点时会反复用到。// 找到以 node 为根的子树中的最小节点 BSTNode* findMin(BSTNode* node) { while (node node-lchild) { node node-lchild; } return node; }也可以使用递归方式实现但这里使用循环更直观只要左孩子还存在就继续向左走。6. 递归删除删除是 BST 中最容易写错的操作核心在于处理目标节点的三种情况叶子节点、只有一个孩子的节点、有两个孩子的节点。// 递归删除 BSTNode* remove(BSTNode* node, int key) { if (node nullptr) return nullptr; if (key node-data) { node-lchild remove(node-lchild, key); } else if (key node-data) { node-rchild remove(node-rchild, key); } else { // 找到了要删除的节点 if (node-lchild nullptr) { // 只有右孩子或没有孩子 BSTNode* temp node-rchild; delete node; return temp; } else if (node-rchild nullptr) { // 只有左孩子 BSTNode* temp node-lchild; delete node; return temp; } else { // 有两个孩子用右子树最小节点替代 BSTNode* temp findMin(node-rchild); node-data temp-data; node-rchild remove(node-rchild, temp-data); } } return node; }三种情况可以总结为叶子节点直接删除。只有一个孩子用孩子顶替自己的位置。有两个孩子找到右子树中最小节点或左子树中最大节点用它的值覆盖当前节点再递归删除那个替身节点。建议删除有两个孩子的节点时一定要在草稿纸上画图模拟。比如删除根节点 50可以取右子树的最小节点 60 替换 50再把原来的 60 删除。7. 中序遍历与销毁中序遍历用于验证二叉排序树的有序性析构函数中需要递归释放整棵树避免内存泄漏。// 中序遍历验证有序性 void inOrder(BSTNode* node) { if (node) { inOrder(node-lchild); cout node-data ; inOrder(node-rchild); } } // 销毁整棵树 void destroy(BSTNode* node) { if (node) { destroy(node-lchild); destroy(node-rchild); delete node; } }后序遍历的思想也可以用于统计节点数量或计算树的高度。它们都属于“先处理子树再处理根节点”的典型递归结构。8. 对外接口类内部用递归实现具体逻辑对外提供简洁的公共接口。调用者不需要关心根指针和递归细节。public: BSTree() : root(nullptr) {} ~BSTree() { destroy(root); } void insert(int key) { root insert(root, key); } bool search(int key) { return search(root, key) ! nullptr; } void remove(int key) { root remove(root, key); } void inOrderTraverse() { cout 中序遍历; inOrder(root); cout endl; } };这样设计的好处是二叉排序树的递归逻辑集中在私有函数中公共接口只负责传入用户数据并更新根节点代码结构清晰也便于后续扩展为 AVL 树等平衡结构。9. 核心操作复杂度分析操作平均时间复杂度最坏时间复杂度说明查找O(log n)O(n)最坏情况下退化为链表插入O(log n)O(n)插入路径与查找路径一致删除O(log n)O(n)删除有两个孩子的节点时较复杂中序遍历O(n)O(n)一定能得到递增有序序列最坏情况通常发生在输入序列本身有序时。例如依次插入{10, 20, 30, 40, 50}二叉排序树会退化成一条链10 \ 20 \ 30 \ 40 \ 50此时查找、插入、删除的时间复杂度都会退化为 O(n)性能与普通链表相同。这也是后续必须学习 AVL 树、红黑树等平衡二叉树的根本原因。10. 完整测试代码下面给出完整的可运行程序覆盖插入、查找、删除和中序遍历。#include iostream #include queue using namespace std; struct BSTNode { int data; BSTNode *lchild, *rchild; BSTNode(int d) : data(d), lchild(nullptr), rchild(nullptr) {} }; class BSTree { private: BSTNode* root; BSTNode* insert(BSTNode* node, int key) { if (node nullptr) { return new BSTNode(key); } if (key node-data) { node-lchild insert(node-lchild, key); } else if (key node-data) { node-rchild insert(node-rchild, key); } return node; } BSTNode* search(BSTNode* node, int key) { if (node nullptr || node-data key) { return node; } if (key node-data) { return search(node-lchild, key); } else { return search(node-rchild, key); } } BSTNode* findMin(BSTNode* node) { while (node node-lchild) { node node-lchild; } return node; } BSTNode* remove(BSTNode* node, int key) { if (node nullptr) return nullptr; if (key node-data) { node-lchild remove(node-lchild, key); } else if (key node-data) { node-rchild remove(node-rchild, key); } else { if (node-lchild nullptr) { BSTNode* temp node-rchild; delete node; return temp; } else if (node-rchild nullptr) { BSTNode* temp node-lchild; delete node; return temp; } else { BSTNode* temp findMin(node-rchild); node-data temp-data; node-rchild remove(node-rchild, temp-data); } } return node; } void inOrder(BSTNode* node) { if (node) { inOrder(node-lchild); cout node-data ; inOrder(node-rchild); } } void destroy(BSTNode* node) { if (node) { destroy(node-lchild); destroy(node-rchild); delete node; } } public: BSTree() : root(nullptr) {} ~BSTree() { destroy(root); } void insert(int key) { root insert(root, key); } bool search(int key) { return search(root, key) ! nullptr; } void remove(int key) { root remove(root, key); } void inOrderTraverse() { cout 中序遍历; inOrder(root); cout endl; } }; int main() { BSTree tree; // 插入 int arr[] {50, 30, 70, 20, 40, 60, 80}; for (int x : arr) { tree.insert(x); } tree.inOrderTraverse(); // 应输出20 30 40 50 60 70 80 // 查找 cout 查找 40 (tree.search(40) ? 找到 : 未找到) endl; cout 查找 90 (tree.search(90) ? 找到 : 未找到) endl; // 删除 tree.remove(30); // 删除有两个孩子的节点 tree.inOrderTraverse(); // 20 40 50 60 70 80 tree.remove(50); // 删除根节点 tree.inOrderTraverse(); return 0; }程序运行结果如下中序遍历20 30 40 50 60 70 80 查找 40找到 查找 90未找到 中序遍历20 40 50 60 70 80 中序遍历20 40 60 70 80删除节点 30 后节点 40 顶替原 30 的位置删除根节点 50 后右子树中的最小节点 60 顶替根节点位置。最终中序遍历结果仍然保持递增有序。11. 删除操作的三种情况图解删除操作可以拆成三种典型情况建议对照代码逐一画图。11.1 删除叶子节点例如删除节点 20删除前 删除后 30 30 / \ / 20 40 40叶子节点没有孩子直接释放该节点并让父节点的对应指针置空。11.2 删除只有一个孩子的节点例如删除节点 30它只有一个右孩子 40删除前 删除后 30 40 \ 40此时只需用它的孩子顶替它自己的位置。11.3 删除有两个孩子的节点例如删除根节点 50删除前 删除后 50 60 / \ / \ 30 70 30 70 / \ / \ / \ \ 20 40 60 80 20 40 80找到右子树中的最小节点 60用 60 覆盖 50然后递归删除原 60。这样既能保持二叉排序树的有序性也避免直接调整大量节点的指针。12. 总结与思考二叉排序树把“查找”和“动态有序”很好地结合在一起平均性能优秀是实现动态查找表的经典结构。但它存在退化风险因此在工程实践中很少直接使用朴素 BST而是使用它的平衡版本如 AVL 树、红黑树、B 树等。结合《C Primer Plus》的思考递归实现插入、查找和删除充分练习了书中关于递归、指针和函数返回值传递的内容。删除时对节点三种情况的处理体现了细致的内存管理和指针维护能力。通过图例模拟树的形态变化有助于理解指针的挂接关系而不是只背代码。下一篇将按顺序继续第8章内容平衡二叉树AVL 树的旋转与实现重点解决朴素 BST 在有序插入时退化为链表的问题。

相关新闻

基于DeepSeek V4与LangChain构建低成本AI数据分析Agent实战

基于DeepSeek V4与LangChain构建低成本AI数据分析Agent实战

最近在尝试将国产大模型DeepSeek V4接入Codex平台,构建一个能够处理真实业务数据的AI数据分析系统时,发现网上资料要么过于零散,要么就是简单的API调用示例,对于如何设计一个完整的、可运行的Agent系统,特别是成本控制…

2026/8/16 3:10:53 阅读更多 →
Python开发工具全解析:从IDE选择到环境配置实战指南

Python开发工具全解析:从IDE选择到环境配置实战指南

1. 为什么Python开发者需要一个趁手的IDE?如果你刚开始学Python,可能觉得用记事本或者简单的文本编辑器写几行代码也能跑起来。但当你真正开始做一个项目,面对几十个文件、需要调试、需要管理依赖、需要版本控制的时候,一个强大的…

2026/8/16 3:10:53 阅读更多 →
一人公司如何用AgentOS构建自动化数字飞轮:架构设计与实战

一人公司如何用AgentOS构建自动化数字飞轮:架构设计与实战

1. 项目概述:一人公司的数字飞轮如何转动最近和几个独立开发者朋友聊天,大家普遍有个共鸣:一个人单打独斗做项目,精力太容易被琐事耗散。今天要处理服务器告警,明天要回复用户反馈,后天还得写更新日志。创意…

2026/8/16 3:09:53 阅读更多 →

最新新闻

小程序体积优化全链路实战:从代码瘦身到分包策略

小程序体积优化全链路实战:从代码瘦身到分包策略

1. 项目概述:小程序体积膨胀的“隐形杀手”最近在帮团队做小程序性能审计,发现一个老生常谈但又极易被忽视的问题:打包体积过大。一个看似简单的商城小程序,动辄就超过2MB的包体限制,甚至逼近20MB的主包上限&#xff0…

2026/8/16 5:42:38 阅读更多 →
2026四大AI论文平台深度测评|学术写作不是堆砌,工具要服务于思维

2026四大AI论文平台深度测评|学术写作不是堆砌,工具要服务于思维

近几年 AI 写论文早已普及,但工具乱用直接踩雷。在学术写作日益依赖技术辅助的今天,不少学生误以为只要用上AI工具就能轻松应对论文压力,却忽视了工具选择与使用方式的科学性。 很多同学分不清通用AI和学术AI的区别,不管是课程作业…

2026/8/16 5:42:38 阅读更多 →
机器学习交叉验证原理与五折交叉验证实践

机器学习交叉验证原理与五折交叉验证实践

1. 为什么我们需要交叉验证?想象一下这样的场景:你正在训练一个机器学习模型来预测房价。你把所有数据分成训练集和测试集,用训练集训练模型,然后在测试集上得到了95%的准确率。看起来很棒,对吧?但当你把模…

2026/8/16 5:42:38 阅读更多 →
告别AI痕迹!降AIGC工具终极测评与精准选型工具箱

告别AI痕迹!降AIGC工具终极测评与精准选型工具箱

2026年,学术写作在AI技术的深度渗透下迎来全新变革。随着AIGC检测机制日益严格,论文中的AI痕迹、重复率超标和学术规范性问题成为研究者必须直面的挑战。如何在保持内容质量的同时,有效降低查重率与AI识别风险,已成为科研工作者的…

2026/8/16 5:42:38 阅读更多 →
图文教程:用国内大模型 API 跑通Codex

图文教程:用国内大模型 API 跑通Codex

今天介绍一个开源工具,能让你在国内网络环境下,用 DeepSeek、Kimi、智谱等国产大模型的 API Key,直接跑通 Codex 的全部能力——读取本地项目、修改代码、执行命令。 这个工具叫 CC-Switch。这篇文章从零开始,讲清楚它是什么、为什么需要它、怎么下载配置、怎么配合 Codex…

2026/8/16 5:42:38 阅读更多 →
FFTW环境搭建全攻略:从源码编译到性能优化实践

FFTW环境搭建全攻略:从源码编译到性能优化实践

1. 项目概述:为什么FFTW值得你花时间搭建环境?如果你正在处理信号处理、图像分析或者科学计算相关的项目,并且被各种傅里叶变换(FFT)的性能问题所困扰,那么FFTW这个名字你应该不陌生。FFTW,全称…

2026/8/16 5:41:38 阅读更多 →

日新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/16 0:00:54 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:55 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/16 0:03:55 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/16 0:00:54 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:55 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/16 0:03:55 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/14 13:40:53 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/14 14:06:45 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/15 2:35:29 阅读更多 →