文章目录
- :red_circle:一、二叉搜索树的概念
- :red_circle:二、二叉搜索树的性能分析
- :red_circle:三、二叉搜索树的操作
- (一)插入
- (二)查找
- (三)删除
- :red_circle:四、二叉搜索树的实现代码
- (一)结构体 `BSTNode`
- (二)类 `BSTree`
- :red_circle:五、二叉搜索树的应用场景
- (一)key搜索场景
- (二)key/value搜索场景
- :red_circle:六、总结
- 如果有帮助的话麻烦点个赞和关注吧,秋梨膏QAQ!
作者的个人gitee
作者的算法讲解主页▶️
每日一言:“人生如逆旅,我亦是行人。🌸🌸”
🔴一、二叉搜索树的概念
二叉搜索树(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
template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key): _key(key), _left(nullptr), _right(nullptr) {}
};
(二)类 BSTree
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;elseparent->_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;elseparent->_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;elseparent->_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;elserightMinP->_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树和红黑树。