文章目录
-
- :red_circle:一、二叉搜索树的概念
- :red_circle:二、二叉搜索树的性能分析
- :red_circle:三、二叉搜索树的操作
- :red_circle:四、二叉搜索树的实现代码
-
- [(一)结构体 `BSTNode`](#(一)结构体
BSTNode
) - [(二)类 `BSTree`](#(二)类
BSTree
)
- [(一)结构体 `BSTNode`](#(一)结构体
- :red_circle:五、二叉搜索树的应用场景
- :red_circle:六、总结
- 如果有帮助的话麻烦点个赞和关注吧,秋梨膏QAQ!
每日一言:"**人生如逆旅,我亦是行人。**🌸🌸"
🔴一、二叉搜索树的概念
二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树,具有以下性质:
- 若左子树不为空,则左子树上所有结点的值都小于等于根结点的值。
- 若右子树不为空,则右子树上所有结点的值都大于等于根结点的值。
- 左右子树也分别为二叉搜索树。
- 二叉搜索树中可以支持插入相等的值,也可以不支持,具体取决于使用场景的定义。如,在C++标准模板库中,
map
和set
不支持插入相等值,而multimap
和multiset
支持插入相等值。

🔴二、二叉搜索树的性能分析
二叉搜索树的性能主要取决于其高度。以下是不同情况下的性能分析:
情况 | 高度 | 增删查改时间复杂度 |
---|---|---|
最优 | logN | O(logN) |
最差 | N | O(N) |
平均 | 取决于插入顺序 | 取决于插入顺序 |
- 最优情况 :当二叉搜索树为完全二叉树或接近完全二叉树时,其高度为log2N。此时增删查改的时间复杂度为 O(log2N)。
- 最差情况 :当二叉搜索树退化为单支树或类似单支时,其高度为N。此时增删查改的时间复杂度为O(N)。
- 平均情况 :在实际应用中,二叉搜索树的高度取决于插入数据的顺序。如果插入数据是随机 的,那么平均情况下二叉搜索树的性能接近最优 情况;但如果插入数据是有序 的,那么二叉搜索树可能会退化为单支树,性能接近最差情况。

🔴三、二叉搜索树的操作
(一)插入
插入操作的步骤如下:
- 树为空:如果二叉搜索树为空,则直接创建一个新的结点,并将其赋值给根指针。
- 树不为空 :从根结点开始,按照二叉搜索树的性质进行比较:
- 如果插入值小于当前结点的值,则向左子树移动。
- 如果插入值大于当前结点的值,则向右子树移动。
- 如果插入值等于当前结点的值(支持插入相等值的情况),可以选择向左或向右移动,但需要保持逻辑一致性。
- 找到空位置:当找到一个空位置时,创建一个新的结点,并将其插入到该位置。
(二)查找
查找操作的步骤如下:
- 从根开始:从根结点开始,将目标值与当前结点的值进行比较。
- 比较并移动 :
- 如果目标值小于当前结点的值,则向左子树移动。
- 如果目标值大于当前结点的值,则向右子树移动。
- 查找结果 :
- 如果在某个结点找到目标值,则返回该结点。
- 如果走到空结点仍未找到目标值,则说明目标值不存在。
(三)删除
删除操作相对复杂,需要分情况处理:
情况 | 描述 | 解决方案 |
---|---|---|
1 | 要删除结点N左右孩子均为空 | 把N结点的父母对应孩子指针指向空,直接删除N结点 |
2 | 要删除的结点N左孩子为空,右孩子结点不为空 | 把N结点的父母对应孩子指针指向N的右孩子,直接删除N结点 |
3 | 要删除的结点N右孩子为空,左孩子结点不为空 | 把N结点的父母对应孩子指针指向N的左孩子,直接删除N结点 |
4 | 要删除的结点N左右孩子结点均不为空 | 无法直接删除N结点,用替换法删除。找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)替代N,然后变成删除R结点,R结点符合情况2或情况3,可以直接删除 |
🔴四、二叉搜索树的实现代码
(一)结构体 BSTNode
cpp
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode(const K& key)
: _key(key), _left(nullptr), _right(nullptr) {}
};
(二)类 BSTree
cpp
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
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->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
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; // 未找到目标值
}
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
{
// 情况1:左右孩子均为空
if (cur->_left == nullptr && cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = nullptr;
}
else
{
if (parent->_left == cur)
parent->_left = nullptr;
else
parent->_right = nullptr;
}
delete cur;
return true;
}
// 情况2:左孩子为空,右孩子不为空
else if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
return true;
}
// 情况3:右孩子为空,左孩子不为空
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;
}
delete cur;
return true;
}
// 情况4:左右孩子均不为空
else
{
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
}
}
return false; // 未找到目标值
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root = nullptr;
};
🔴五、二叉搜索树的应用场景
(一)key搜索场景
在key搜索场景中,二叉搜索树仅存储关键码(key),用于判断某个值是否存在。
- 小区无人值守车库:将买了车位的业主车牌号录入后台系统,车辆进入时扫描车牌号,判断其是否存在于系统中,从而决定是否抬杆。
- 英文单词拼写检查:将词库中的所有单词放入二叉搜索树,读取文章中的单词,查找其是否存在于二叉搜索树中,若不存在则提示拼写错误。
(二)key/value搜索场景
在key/value搜索场景中,二叉搜索树的每个结点不仅存储关键码(key),还存储与之对应的值(value)。搜索时以key为关键字进行比较,可以快速找到key对应的value。
- 中英互译字典:在树的结点中存储英文单词(key)和对应的中文翻译(value),搜索时输入英文单词,即可找到其对应的中文翻译。
- 商场无人值守车库:入口进场时扫描车牌号,记录车牌号和入场时间(key/value),出口离场时扫描车牌号,查找入场时间,计算停车时长和费用。
- 统计文章中单词出现次数:读取一个单词,查找其是否存在于二叉搜索树中,若不存在则插入该单词并将其出现次数初始化为1,若存在则将其对应的出现次数加1。
🔴六、总结
二叉搜索树是一种重要的数据结构,具有插入、查找、删除等操作。其性能在最优情况下接近O(log2N),但在最差情况下会退化为O(N)。为了提高二叉搜索树的性能,后续可以学习其进阶,如平衡二叉搜索树(AVL树)、B树和红黑树。