41人参与 • 2026-02-15 • C/C++
前言
二叉搜索树(binary search tree,bst)作为一种经典的树形数据结构,凭借其高效的动态查找、插入和删除特性,在计算机科学领域有着广泛的应用。从底层实现来看,c++ 标准库中的map、set、multimap、multiset等关联式容器,其核心逻辑正是基于二叉搜索树(红黑树作为其平衡优化版本)构建。相较于面向对象编程中的多态特性(侧重行为的动态绑定与代码复用),二叉搜索树聚焦于数据的有序存储与高效检索,其核心价值在于利用 “左子树值≤根节点值≤右子树值” 的结构性约束,将查找、插入、删除操作的时间复杂度控制在近似 o(logn)(理想的平衡状态下);而在最坏的单支树场景下,时间复杂度退化为 o(n),这也体现了数据结构设计中 “结构与性能” 的强关联性。
本文将从二叉搜索树的核心定义出发,逐步拆解节点设计、树的构建、插入、查找、删除等核心操作的实现逻辑,并区分 “仅存关键码(key)” 与 “键值对(key/value)” 两种典型应用场景,最终给出完整的可运行代码实现。代码实现过程中兼顾 c++11 语法特性(如
using类型别名)、代码封装性(私有成员访问控制)与逻辑健壮性(边界条件处理),力求在清晰性与工程性之间找到平衡。
二叉搜索树又称二叉排序树,满足以下核心特性:
map、set、multimap、multiset系列的如期底层就是二叉搜索树,其中map、set不支持插入相等值,multimap、multiset支持插入相等的值。
最好情况:树的结构接近完全二叉树,高度为 logn,此时查找、插入、删除操作的时间复杂度为 o(logn);
最坏情况:树退化为单支树,高度为 n,此时操作的时间复杂度退化为 o(n)。

二叉搜索树本质是由节点连接而成的链式结构,每个节点包含关键码、左孩子指针、右孩子指针,具体实现如下:
#pragma once
#include<iostream>
using namespace std;
// 二叉搜索树节点结构
template<class k>
struct bstnode
{
k _key; // 节点存储的关键码
bstnode<k>* _left; // 左孩子节点指针
bstnode<k>* _right; // 右孩子节点指针
// 构造函数:初始化节点
bstnode(const k& key)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{
}
};将二叉搜索树封装为类,根节点作为私有成员(保证数据封装性),使用 c++11 的 using 简化节点类型名:
template<class k>
class bstree
{
using node = bstnode<k>; // c++11 类型别名,替代传统 typedef
public:
// 核心操作声明(插入、查找、删除、中序遍历)
bool insert(const k& key);
bool find(const k& key);
bool erase(const k& key);
void inorder();
private:
// 私有辅助函数:中序遍历的递归实现
void _inorder(node* root);
node* _root = nullptr; // 根节点指针,初始化为空
};插入逻辑:

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};


template<class k>
bool bstree<k>::insert(const k& key)
{
// 情况1:树为空,直接创建根节点
if (_root == nullptr)
{
_root = new node(key);
return true;
}
// 情况2:树不为空,遍历找到插入位置
node* cur = _root;
node* parent = nullptr; // 记录当前节点的父节点(用于后续连接新节点)
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->_right = cur; // 新节点值更大,连接到父节点右孩子
}
else
{
parent->_left = cur; // 新节点值更小,连接到父节点左孩子
}
return true;
}
template<class k>
bool bstree<k>::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; // 遍历至空,查找失败(补充原代码缺失的返回值)
}删除逻辑:
左右孩子都不为空:采用 “替换法”—— 找右子树的最小节点(最左节点)或左子树的最大节点(最右节点)替换目标节点,再删除替换节点(替换节点必为单孩子 / 叶子节点)。



template<class k>
bool bstree<k>::erase(const k& key)
{
node* cur = _root;
node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else // 找到要删除的节点
{
// 情况1:左孩子为空(叶子节点/仅右孩子)
if (cur->_left == nullptr)
{
// 处理根节点删除的特殊情况
if (cur == _root)
{
_root = cur->_right;
}
else
{
// 父节点的左/右指针指向当前节点的右孩子
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur; // 释放节点内存
}
// 情况2:右孩子为空(叶子节点/仅左孩子)
else if (cur->_right == nullptr)
{
// 处理根节点删除的特殊情况
if (cur == _root)
{
_root = cur->_left;
}
else
{
// 父节点的左/右指针指向当前节点的左孩子
if (parent->_right == cur)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur; // 释放节点内存
}
// 情况3:左右孩子都不为空(替换法删除)
else
{
// 找右子树的最小节点(最左节点)作为替换节点
node* replaceparent = cur;
node* replace = cur->_right;
while (replace->_left) // 遍历至右子树最左节点
{
replaceparent = replace;
replace = replace->_left;
}
// 替换目标节点的关键码
cur->_key = replace->_key;
// 连接替换节点的父节点与替换节点的右孩子(替换节点左必为空)
if (replaceparent->_left == replace)
{
replaceparent->_left = replace->_right;
}
else
{
replaceparent->_right = replace->_right;
}
delete replace; // 释放替换节点内存
}
return true; // 删除成功
}
}
return false; // 未找到目标节点,删除失败
}二叉搜索树的中序遍历结果为升序序列,是验证树结构正确性的核心方式。通过 “公有接口 + 私有递归函数” 的方式访问私有根节点:
template<class k>
void bstree<k>::_inorder(node* root)
{
if (root == nullptr)
{
return;
}
_inorder(root->_left); // 遍历左子树
cout << root->_key << " "; // 访问当前节点
_inorder(root->_right); // 遍历右子树
}
template<class k>
void bstree<k>::inorder()
{
_inorder(_root); // 调用私有递归函数
cout << endl;
}节点仅存储关键码 key,操作仅关注 “key 是否存在”,不支持修改 key(修改会破坏树的结构),支持增删查。
节点存储 key + value(value 为任意类型),增删查以 key 为关键字,支持修改 value(不修改 key)。
// 键值对版本的节点结构
template<class k, class t>
struct bstnodekv
{
k _key;
t _value;
bstnodekv<k, t>* _left;
bstnodekv<k, t>* _right;
bstnodekv(const k& key, const t& value)
:_key(key)
, _value(value)
, _left(nullptr)
, _right(nullptr)
{
}
};
// 键值对版本的二叉搜索树
template<class k, class t>
class bstreekv
{
using node = bstnodekv<k, t>;
public:
// 插入键值对
bool insert(const k& key, const t& value)
{
if (_root == nullptr)
{
_root = new node(key, value);
return true;
}
node* cur = _root;
node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false; // 不支持重复key
}
}
cur = new node(key, value);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
// 查找key并返回value的指针(方便修改value)
t* 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 &(cur->_value); // 返回value地址,支持修改
}
}
return nullptr; // 查找失败
}
// 删除(逻辑与key版本一致,略)
bool erase(const k& key)
{
node* cur = _root;
node* parent = nullptr;
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)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur)
parent->_right = cur->_left;
else
parent->_left = cur->_left;
}
delete cur;
}
else
{
node* replaceparent = cur;
node* replace = cur->_right;
while (replace->_left)
{
replaceparent = replace;
replace = replace->_left;
}
// 替换key和value
cur->_key = replace->_key;
cur->_value = replace->_value;
if (replaceparent->_left == replace)
replaceparent->_left = replace->_right;
else
replaceparent->_right = replace->_right;
delete replace;
}
return true;
}
}
return false;
}
// 中序遍历(打印key和value)
void inorder()
{
_inorder(_root);
cout << endl;
}
private:
void _inorder(node* root)
{
if (root == nullptr)
return;
_inorder(root->_left);
cout << "key: " << root->_key << ", value: " << root->_value << " ";
_inorder(root->_right);
}
node* _root = nullptr;
};代码规范性优化:
replaceparent 替代原 parent,避免歧义);功能增强:
value 指针,支持修改 value;// 测试key版本的二叉搜索树
void testbstree()
{
bstree<int> t;
int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
for (auto e : a)
{
t.insert(e);
}
cout << "中序遍历(升序):";
t.inorder(); // 输出:1 3 4 6 7 8 10 13 14
cout << "查找6:" << (t.find(6) ? "成功" : "失败") << endl; // 成功
cout << "删除3:" << (t.erase(3) ? "成功" : "失败") << endl; // 成功
cout << "删除后中序遍历:";
t.inorder(); // 输出:1 4 6 7 8 10 13 14
}
// 测试键值对版本的二叉搜索树
void testbstreekv()
{
bstreekv<string, int> dict;
dict.insert("apple", 1);
dict.insert("banana", 2);
dict.insert("orange", 3);
cout << "中序遍历键值对:";
dict.inorder(); // 输出:key: apple, value: 1 key: banana, value: 2 key: orange, value: 3
// 修改banana的value
int* p = dict.find("banana");
if (p)
{
*p = 20;
}
cout << "修改后中序遍历:";
dict.inorder(); // 输出:key: apple, value: 1 key: banana, value: 20 key: orange, value: 3
}
int main()
{
testbstree();
testbstreekv();
return 0;
}二叉搜索树的核心是 “有序性”,其所有操作均围绕这一特性展开。本文实现的基础版本覆盖了二叉搜索树的核心功能,而实际工程中(如 c++ 标准库)会通过红黑树对其进行平衡优化,避免单支树的性能退化。理解二叉搜索树的底层逻辑,是掌握关联式容器、高效检索算法的关键基础。
到此这篇关于【c++】模拟实现 二叉搜索树的文章就介绍到这了,更多相关c++二叉搜索树内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论