跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 二叉搜索树详解:概念、操作与递归实现

二叉搜索树(BST)是 map 和 set 的基础结构,掌握其查找、插入及删除逻辑对理解 C++ STL 至关重要。详细解析 BST 的定义特性,通过非递归与递归两种方式实现核心操作,重点讲解删除节点时保持树性质的替换法策略。此外,涵盖拷贝构造、赋值运算符重载及析构函数的完整类设计,确保内存安全。适合希望深入理解树形数据结构及 C++ 内存管理的开发者阅读。

数字游民发布于 2025/10/5更新于 2026/10/25102 浏览
C++ 二叉搜索树详解:概念、操作与递归实现

C++ 二叉搜索树详解

一、前言

二叉搜索树(BST)是理解 map 与 set 等关联容器的重要基础。相比前期数据结构课程,本节将结合 C++ 特性深入探讨 BST 的进阶应用,特别是针对部分 OJ 题目中动态数组操作的优化场景。

二、二叉搜索树的概念

二叉搜索树或者是一棵空树,或者是具有以下性质的二叉树:

  • 若左子树不为空,则左子树上所有节点的值均小于根节点的值;
  • 若右子树不为空,则右子树上所有节点的值均大于根节点的值;
  • 左右子树也分别为二叉搜索树。

BST 示意图

三、核心操作实现

1. 节点与树的定义
template<class T> class BSTreeNode {
public:
    BSTreeNode<T>* _left;
    BSTreeNode<T>* _right;
    T _key;
public:
    BSTreeNode(const T& val = T()) : _left(nullptr), _right(nullptr), _key(val) {}
};

template<class T> class BSTree {
    typedef BSTreeNode<T> Node;
public:
    BSTree() : _root(nullptr) {}
private:
    Node* _root;
};
2. 查找操作

利用 BST 性质,从根节点开始比较:目标值小向左,大向右,相等则找到。

bool find(const T& val) {
    Node* cur = _root;
    while (cur) {
        if (cur->_key > val) cur = cur->_left;
        else  (cur->_key < val) cur = cur->_right;
          ;
    }
     ;
}
if
else
return
true
return
false
3. 插入操作

需保证树不空时按性质寻找位置,且不允许重复值。

bool insert(const T& val) {
    if (_root == nullptr) { _root = new Node(val); return true; }
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur != nullptr) {
        if (cur->_key > val) { parent = cur; cur = cur->_left; }
        else if (cur->_key < val) { parent = cur; cur = cur->_right; }
        else return false; // 已存在
    }
    Node* newNode = new Node(val);
    if (parent->_key < val) parent->_right = newNode;
    else parent->_left = newNode;
    return true;
}
4. 删除操作(难点)

删除后必须维持 BST 性质。分三种情况:无孩子、单孩子、双孩子。双孩子时通常用'左子树最大值'或'右子树最小值'替换待删节点值,再删除该替换节点。

bool erase(const T& val) {
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur != nullptr) {
        if (cur->_key > val) { parent = cur; cur = cur->_left; }
        else if (cur->_key < val) { parent = cur; cur = cur->_right; }
        else {
            // 情况 1:无左孩子
            if (cur->_left == nullptr) {
                if (parent == nullptr) _root = cur->_right;
                else if (parent->_left == cur) parent->_left = cur->_right;
                else parent->_right = cur->_right;
            }
            // 情况 2:无右孩子
            else if (cur->_right == nullptr) {
                if (parent == nullptr) _root = cur->_left;
                else if (parent->_left == cur) parent->_left = cur->_left;
                else parent->_right = cur->_left;
            }
            // 情况 3:有左右孩子
            else {
                Node* lmaxp = cur;
                Node* lmax = cur->_left;
                while (lmax->_right != nullptr) {
                    lmaxp = lmax;
                    lmax = lmax->_right;
                }
                swap(cur->_key, lmax->_key);
                cur = lmax; // 准备删除 lmax
            }
            delete cur;
            return true;
        }
    }
    return false;
}

测试时可配合中序遍历验证有序性。

四、递归实现

递归写法更简洁,但需注意指针引用传递。

bool _findR(Node* root, const T& key) {
    if (!root) return false;
    if (root->_key < key) return _findR(root->_right, key);
    else if (root->_key > key) return _findR(root->_left, key);
    else return true;
}

关于 Node*& root 的思考: 递归修改树结构时,参数必须是引用。否则函数内修改的是局部副本,无法更新父节点的指针域,导致树结构断裂或内存泄漏。

bool _insertR(Node*& root, const T& key) {
    if (!root) { root = new Node(key); return true; }
    if (root->_key < key) return _insertR(root->_right, key);
    else if (root->_key > key) return _insertR(root->_left, key);
    else return false;
}

删除同理,递归版本同样需要处理替换逻辑。

五、完善类成员

遵循 Rule of Three,需实现拷贝构造、赋值运算符和析构函数。

// 拷贝构造
BSTree(const BSTree<T>& t) { _root = Copy(t._root); }
Node* Copy(Node* root) {
    if (!root) return nullptr;
    Node* copyRoot = new Node(root->_key);
    copyRoot->_left = Copy(root->_left);
    copyRoot->_right = Copy(root->_right);
    return copyRoot;
}

// 赋值运算符重载
BSTree<T>& operator=(BSTree<T> t) {
    swap(_root, t._root);
    return *this;
}

// 析构函数
~BSTree() { Destroy(_root); }
void Destroy(Node*& root) {
    if (!root) return;
    Destroy(root->_left);
    Destroy(root->_right);
    delete root;
    root = nullptr;
}

至此,一个完整的二叉搜索树模板类基本成型。实际开发中可根据需求进一步优化平衡性(如 AVL 树或红黑树)。

目录

  1. C++ 二叉搜索树详解
  2. 一、前言
  3. 二、二叉搜索树的概念
  4. 三、核心操作实现
  5. 1. 节点与树的定义
  6. 2. 查找操作
  7. 3. 插入操作
  8. 4. 删除操作(难点)
  9. 四、递归实现
  10. 五、完善类成员

更多推荐文章

查看全部
  • 双指针算法实战:移动零与复写零详解
  • CycleGAN 详解与实现:无配对图像风格转换
  • OpenClaw 接入企业微信实战指南
  • C++ STL list 容器详解:使用与模拟实现
  • macOS Web:基于 Web 技术的 macOS 桌面模拟器
  • 无模型自适应控制算法原理及与 PID 对比验证
  • OpenClaw 全平台卸载指南:Windows、macOS、Linux 及包管理器清理
  • 2026 年 3 月 18 日人工智能前沿动态与产业观察
  • Cursor 编辑器 C/C++ 代码跳转问题解决方案
  • Rust 错误处理与测试:打造健壮可维护应用的核心实践
  • NASA 火星任务软件测试:利用 AIGC 模拟极端环境攻击
  • 前端文件下载实战:从原理到最佳实践
  • AI Agent 基础架构与核心组件详解
  • C++11 核心特性:列表初始化与右值引用移动语义详解
  • Node.js 安装指南(macOS 版)
  • 2024 年转行 AI 产品经理的时机与准备指南
  • Python PyQt5 安装与配置及界面设计教程
  • 基于 ChatGLM-6B 的医疗领域大模型微调实战指南
  • 内容创作模式解析:UGC、PGC、PUGC、OGC、MGC、BGC 与 AIGC
  • 双指针算法专题:移动零与复写零

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online