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

二叉搜索树 C++ 实现:增删查改详解

二叉搜索树(BST)是一种特殊的二叉树结构,其左子树节点值均小于根节点,右子树节点值均大于根节点。该实现通过 C++ 模板类完整演示了 BST 的核心操作,包括插入、查找、中序遍历及复杂的删除逻辑。针对单键值与键值对两种场景分别实现了节点结构与类接口,重点剖析了删除节点时处理左右子树非空情况的替换策略。理解 BST 的实现细节有助于深入掌握红黑树等平衡树容器的底层原理。

随缘发布于 2026/3/27更新于 2026/10/2376 浏览
二叉搜索树 C++ 实现:增删查改详解

什么是二叉搜索树

二叉搜索树(BST)又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 左子树上所有节点的值都小于等于根结点的值;
  • 右子树上所有节点的值都大于等于根结点的值;
  • 它的左右子树也分别为二叉搜索树。

二叉搜索树示意图

注意:二叉搜索树中可以支持插入相等的值,也可以不支持。具体取决于使用场景。例如 std::map/std::set 不支持插入相等值,而 std::multimap/std::multiset 支持。

性能分析

在最优情况下,二叉搜索树为完全二叉树(或接近完全二叉树),其高度为 log₂N。最差情况下,二叉搜索树退化为单支树(类似链表),其高度为 N。

性能对比图

所以平均时间复杂度为 O(log n),最差情况为 O(n)。

虽然二分查找也能实现 O(log₂N) 级别的查找效率,但二分查找有两大缺陷:

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

Key 类型二叉搜索树的实现

为什么是 Key 类型?因为我们后面要讲的 std::set/std::multiset 容器的底层数据结构就是红黑树——一种'近似平衡'的二叉搜索树。对于 set/multiset 容器,集合是一种容器,它按照特定顺序存储唯一的元素。

Set 结构图

下面实现过程中默认为值不重复的情形。

节点与类结构

template<class K> struct BSTNode {
    K _key;
    BSTNode<K>* _left;
    BSTNode<K>* _right;

    BSTNode(const K& key = 0) :_key(key), _left(nullptr), _right(nullptr) {}
};

template< >   {
     Node = BSTNode<K>;
:
    
:
    Node* _root = ;
};
class
K
class
BSTree
using
public
// ... 成员函数声明
private
nullptr

插入操作

思路很简单:

  1. 树为空,直接新增结点,赋值给 root 指针。
  2. 树不空,按二叉搜索树性质,用 cur 指针遍历,parent 指针记下父亲节点。要插入值 key 比当前节点大往右走,小往左走,直到找到空位置插入新结点。
  3. 确定新节点插在 parent 的左边还是右边。
bool Insert(const K& key) {
    Node* node = new Node(key);
    if (_root == nullptr) {
        _root = node;
        return true;
    }
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur) {
        if (key < cur->_key) {
            parent = cur;
            cur = cur->_left;
        } else if (key > cur->_key) {
            parent = cur;
            cur = cur->_right;
        } else {
            return false; // 不允许重复
        }
    }
    if (key < parent->_key) {
        parent->_left = node;
    } else {
        parent->_right = node;
    }
    return true;
}

中序遍历

根据二叉搜索树的特性,其中序遍历的结果恰好为一个有序序列(升序)。由于 _root 是成员变量,在类外面调用无法直接传参,所以采用内部递归辅助函数的形式。

void MidOrder() { _MidOrder(_root); }

void _MidOrder(Node* _root) {
    if (_root == nullptr) return;
    _MidOrder(_root->_left);
    cout << _root->_key << " ";
    _MidOrder(_root->_right);
}

查找

从根开始比较,val 比根大则往右找,小则往左找。最多查找高度次,走到空还没找到就返回 false。

bool Find(const K& val) {
    Node* cur = _root;
    while (cur) {
        if (val < cur->_key) {
            cur = cur->_left;
        } else if (val > cur->_key) {
            cur = cur->_right;
        } else {
            return true;
        }
    }
    return false;
}

删除操作

删除是最复杂的,主要分三种情况处理:

  1. 左为空:将父节点的指针指向该节点的右孩子。
  2. 右为空:将父节点的指针指向该节点的左孩子。
  3. 左右都不为空:无法直接删除,只能用替换法。找左子树最大值(最右结点)或右子树最小值(最左结点)替代被删节点,交换值后转为删除那个替代节点(此时替代节点必属于情况 1 或 2)。

删除逻辑图

bool Erase(const K& val) {
    Node* cur = _root;
    Node* parent = nullptr;
    while (cur) {
        if (val < cur->_key) {
            parent = cur;
            cur = cur->_left;
        } else if (val > cur->_key) {
            parent = cur;
            cur = cur->_right;
        } else {
            // 左为空
            if (cur->_left == nullptr) {
                if (cur == _root) _root = _root->_right;
                else if (cur == parent->_left) parent->_left = cur->_right;
                else parent->_right = cur->_right;
                delete cur;
            }
            // 右为空
            else if (cur->_right == nullptr) {
                if (cur == _root) _root = _root->_left;
                else if (cur == parent->_left) parent->_left = cur->_left;
                else parent->_right = cur->_left;
                delete cur;
            }
            // 左右均不为空
            else {
                Node* replaceParent = cur;
                Node* replace = cur->_right;
                // 找右子树中最小值的节点 R
                while (replace->_left) {
                    replaceParent = replace;
                    replace = replace->_left;
                }
                cur->_key = replace->_key;
                if (replace == replaceParent->_left) {
                    replaceParent->_left = replace->_right;
                } else {
                    cur->_right = replace->_right;
                }
                delete replace;
            }
            return true;
        }
    }
    return false;
}

Key_Value 类型二叉搜索树的实现

对于 std::map/std::multimap 容器,映射是一种关联容器,它按照特定顺序存储由'键值(key value)'和'映射值(mapped value)'组合而成的元素。

Map 结构图

与 Key 类型相比,只是多了个 _value。除了插入操作外,其他操作基本相同,重点补充构造、析构等生命周期管理。

节点与类结构

template<class K, class V> struct BSTNode {
    K _key;
    V _value;
    BSTNode<K, V>* _left;
    BSTNode<K, V>* _right;
    BSTNode(const K& key = 0, const V& value = 0) :_key(key), _value(value), _left(nullptr), _right(nullptr) {}
};

template<class K, class V> class BSTree {
    using Node = BSTNode<K, V>;
public:
    // ...
private:
    Node* _root = nullptr;
};

构造函数与拷贝构造

C++11 强制生成默认构造,拷贝构造需要深拷贝。

// 默认构造
BSTree() = default;

// 拷贝构造(深拷贝)
BSTree(const BSTree& tree) {
    _root = copy(tree._root);
}
Node* copy(Node* root) {
    if (root == nullptr) return nullptr;
    Node* newroot = new Node(root->_key, root->_value);
    newroot->_left = copy(root->_left);
    newroot->_right = copy(root->_right);
    return newroot;
}

赋值重载

现代写法利用 swap 实现自我赋值安全及资源转移。

BSTree& operator=(BSTree tree) {
    swap(_root, tree._root);
    return *this;
}

析构函数

后序遍历释放内存,防止根结点被释放而找不到左右孩子节点。

~BSTree() {
    Destory(_root);
    _root = nullptr;
}
void Destory(Node* root) {
    if (root == nullptr) return;
    Destory(root->_left);
    Destory(root->_right);
    delete root;
}

插入

bool Insert(const K& key, const V& value) {
    Node* node = new Node(key, value);
    if (_root == nullptr) {
        _root = node;
        return true;
    }
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur) {
        if (key < cur->_key) {
            parent = cur;
            cur = cur->_left;
        } else if (key > cur->_key) {
            parent = cur;
            cur = cur->_right;
        } else {
            return false;
        }
    }
    if (key < parent->_key) {
        parent->_left = node;
    } else {
        parent->_right = node;
    }
    return true;
}

总结

本文简单实现了二叉搜索树的基础版本。它与 set/multiset/map/multimap 底层的数据结构——平衡二叉搜索树有所不同,并没有过多关注底层实现细节,比如 insert、Find 返回值等。目的是为了能够加深大家对二叉搜索树的理解,方便后续对平衡二叉树的学习。

总结图

目录

  1. 什么是二叉搜索树
  2. 性能分析
  3. Key 类型二叉搜索树的实现
  4. 节点与类结构
  5. 插入操作
  6. 中序遍历
  7. 查找
  8. 删除操作
  9. Key_Value 类型二叉搜索树的实现
  10. 节点与类结构
  11. 构造函数与拷贝构造
  12. 赋值重载
  13. 析构函数
  14. 插入
  15. 总结

更多推荐文章

查看全部
  • 2026 年 AI 生成产品原型工具实测:墨刀、FigmaMake、Uizard 对比
  • 基于 Docker 部署 Jenkins 与 Playwright 实现 Web 自动化测试
  • Claude Code vs GitHub Copilot CLI 终端开发深度评测
  • GitHub 项目上传实战指南
  • 动态规划路径类 DP 入门:最小路径和、迷雾森林与过河卒
  • 网络安全学习资源与常用安全论坛汇总
  • 2026 年主流 AI Agent 工具全景梳理与选型指南
  • AI 绘画与摄影:ChatGPT、Midjourney 与文心一格工具解析
  • Shell 脚本封装 Curl 请求及响应码校验
  • FastGPT 结合 MCP 协议构建工具增强型智能体实践
  • 初阶数据结构:常见排序算法原理与实现(上)
  • OpenClaw 开源桌面 Agent 部署与飞书钉钉集成实战指南
  • 利用腾讯云 HAI 与 DeepSeek 快速构建个人网页
  • 基于腾讯云 HAI 与 DeepSeek 快速构建个人网页
  • ROS1 全局参数:定义、获取与修改实战
  • 本地电脑训练专属大模型实战教程
  • 预训练语言模型与 BERT 实战应用
  • 基于 Java 的家政服务管理系统的设计与实现
  • Java JDK 安装与环境配置教程(Windows + macOS 通用)
  • Ubuntu 25.04 私有化部署 Ollama + DeepSeek + OpenWebUI 实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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