目录
[一、 二叉搜索树的概念与特性](#一、 二叉搜索树的概念与特性)
[二、 二叉搜索树的性能分析](#二、 二叉搜索树的性能分析)
[3.1 插入操作](#3.1 插入操作)
[3.2 查找操作](#3.2 查找操作)
[3.3 删除操作](#3.3 删除操作)
[4.1 基础版本(Key模型)](#4.1 基础版本(Key模型))
[4.2 扩展版本(Key-Value模型)](#4.2 扩展版本(Key-Value模型))
[5.1 key搜索场景](#5.1 key搜索场景)
[5.2 Key-Value模型应用场景](#5.2 Key-Value模型应用场景)
本文将详细解析二叉搜索树的核心概念、操作实现及应用场景,帮助读者全面掌握这一重要的数据结构。
一、 二叉搜索树的概念与特性
二叉搜索树(Binary Search Tree,BST),又称二叉排序树,是一种特殊的二叉树数据结构,它具有以下性质:
- 
若左子树非空,则左子树上所有节点的值都小于等于根节点的值 
- 
若右子树非空,则右子树上所有节点的值都大于等于根节点的值 
- 
左右子树也分别为二叉搜索树 
重要说明:关于相等值的处理,二叉搜索树可以设计为支持或不支持重复值,这取决于具体应用场景。在C++ STL中:
- 
map/set:不支持重复键值 
- 
multimap/multiset:支持重复键值 

二、 二叉搜索树的性能分析
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其高度为:log₂N
最差情况下,二叉搜索树退化为单支树(或者类似单支树),其高度为:N
所以综合而言,二叉搜索树增删查改时间复杂度为:O(N)
那么这样的效率显然是无法满足我们需求的,我们后续课程需要继续讲解二叉搜索树的变形,平衡二叉树。二叉搜索树、AVL树和红黑树,才能适用于我们在内存中存储和搜索数据。
另外需要说明的是,二分查找也可以实现O(log₂N)级别的查找效率,但是二分查找有两大缺陷:
- 
需要存储在支持下标随机访问的结构中,并且有序。 
- 
插入和删除数据效率很低,因为存储在支持下标随机访问的结构中,插入和删除数据一般需要挪动数据。 
| 情况 | 树形态 | 树高度 | 操作时间复杂度 | 
|---|---|---|---|
| 最优 | 完全二叉树 | O(log₂N) | O(log₂N) | 
| 最差 | 单支树 | O(N) | O(N) | 

三、二叉搜索树的核心操作
3.1 插入操作
如下:
- 
树为空,创建新节点作为根节点 
- 
树非空,从根节点开始比较: 
- 
插入值 > 当前节点值:向右子树移动 
- 
插入值 < 当前节点值:向左子树移动 
- 
插入值 = 当前节点值:根据设计决定(支持重复值则继续移动,否则返回失败) 
- 找到空位置后插入新节点



代码实现:
            
            
              cpp
              
              
            
          
          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;
}3.2 查找操作
步骤:
- 
从根节点开始比较 
- 
查找值 > 当前节点值:向右子树查找 
- 
查找值 < 当前节点值:向左子树查找 
- 
查找值 = 当前节点值:找到目标 
- 
遇到空节点:查找失败 

代码实现:
            
            
              cpp
              
              
            
          
          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;
}3.3 删除操作
首先查找元素是否在二叉搜索树中,如果不存在,则返回false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)
情况1:删除叶子节点
直接删除,父节点对应指针置空
情况2:删除只有右子树的节点
用右子树替代被删节点
情况3:删除只有左子树的节点
用左子树替代被删节点
情况4:删除有两个子树的节点(最复杂)
使用替换法删除:
- 
找到左子树的最大节点或右子树的最小节点 
- 
用找到的节点值替换待删除节点的值 
- 
删除被替换的节点(该节点必定是情况1、2或3) 



代码实现:
            
            
              cpp
              
              
            
          
          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 {
            // 找到要删除的节点
            if (cur->_left == nullptr) {
                // 情况1、2:无左子树
                if (parent == nullptr) {
                    _root = cur->_right;
                } else {
                    if (parent->_left == cur)
                        parent->_left = cur->_right;
                    else
                        parent->_right = cur->_right;
                }
                delete cur;
            } else if (cur->_right == nullptr) {
                // 情况3:无右子树
                if (parent == nullptr) {
                    _root = cur->_left;
                } else {
                    if (parent->_left == cur)
                        parent->_left = cur->_left;
                    else
                        parent->_right = cur->_left;
                }
                delete cur;
            } else {
                // 情况4:有两个子树
                // 找右子树的最小节点
                Node* rightMinParent = cur;
                Node* rightMin = cur->_right;
                while (rightMin->_left) {
                    rightMinParent = rightMin;
                    rightMin = rightMin->_left;
                }
                
                // 替换值
                cur->_key = rightMin->_key;
                
                // 删除右子树的最小节点
                if (rightMinParent->_left == rightMin)
                    rightMinParent->_left = rightMin->_right;
                else
                    rightMinParent->_right = rightMin->_right;
                    
                delete rightMin;
            }
            return true;
        }
    }
    return false;
}四、二叉搜索树的完整实现
4.1 基础版本(Key模型)
Key模型 是最基础的版本控制方式,主要用于管理简单的键值对数据。它的核心特点包括:
1. 数据结构
- 
采用最简单的键值对存储方式 
- 
每个Key对应一个Value 
- 
Value可以是字符串、数字等基本数据类型 
2. 版本控制机制
- 
每次修改都会生成新的版本记录 
- 
通过时间戳或版本号标识不同版本 
- 
支持基本的版本回退功能 
3. 典型应用场景
- 
配置管理:如保存应用的参数配置 
- 
用户偏好设置:存储用户的自定义设置 
- 
简单的元数据管理:如产品属性、标签等 
            
            
              cpp
              
              
            
          
          template<class K>
struct BSTNode {
    K _key;
    BSTNode<K>* _left;
    BSTNode<K>* _right;
    BSTNode(const K& key)
        : _key(key), _left(nullptr), _right(nullptr) {}
};
template<class K>
class BSTree {
    typedef BSTNode<K> Node;
private:
    Node* _root = nullptr;
    
    // 递归中序遍历
    void _InOrder(Node* root) {
        if (root == nullptr) return;
        _InOrder(root->_left);
        std::cout << root->_key << " ";
        _InOrder(root->_right);
    }
    
public:
    // 插入、查找、删除等方法如前文所示
    void InOrder() {
        _InOrder(_root);
        std::cout << std::endl;
    }
};4.2 扩展版本(Key-Value模型)
在标准版本的基础上,我们引入了Key-Value存储模型来增强数据结构的灵活性和查询效率。这个扩展版本特别适合需要快速查找和动态更新的应用场景。
主要特性:
- 
灵活的键值对存储:每个数据项都由唯一的键(Key)和对应的值(Value)组成 - 
键通常采用字符串或数字类型 
- 
值可以是任意数据结构(字符串、数字、数组、对象等) 
- 
示例:{"user_id": 123, "name": "张三", "orders": [1001,1002]} 
 
- 
- 
高效查询机制: - 
支持精确键查询(O(1)时间复杂度) 
- 
可选的二级索引支持范围查询 
 
- 
            
            
              cpp
              
              
            
          
          template<class K, class V>
struct BSTNode {
    K _key;
    V _value;
    BSTNode<K, V>* _left;
    BSTNode<K, V>* _right;
    BSTNode(const K& key, const V& value)
        : _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};
template<class K, class V>
class BSTree {
    typedef BSTNode<K, V> Node;
private:
    Node* _root = nullptr;
    
public:
    // 插入方法
    bool Insert(const K& key, const V& value) {
        if (_root == nullptr) {
            _root = new Node(key, value);
            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, value);
        if (parent->_key < key) {
            parent->_right = cur;
        } else {
            parent->_left = cur;
        }
        return true;
    }
    
    // 查找方法,返回节点指针以便修改value
    Node* 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;
            }
        }
        return nullptr;
    }
};五、二叉搜索树的应用场景
5.1 key搜索场景
场景特点:只需要判断关键字是否存在,不关心关联值。
只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。
场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。

代码实现:
            
            
              cpp
              
              
            
          
          // 车牌检查系统
BSTree<std::string> carSystem;
carSystem.Insert("京A12345");
carSystem.Insert("沪B67890");
// 车辆入场检查
std::string licensePlate = "京A12345";
if (carSystem.Find(licensePlate)) {
    std::cout << "允许进入" << std::endl;
} else {
    std::cout << "未授权车辆" << std::endl;
}5.2 Key-Value模型应用场景
场景特点:需要通过关键字查找对应的关联值。
每一个关键码key,都有与之对应的值value,value可以是任意类型对象。树的结构中(结点)除了需要存储key,还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉搜索树支持修改,但是不支持修改key,修改key会破坏搜索树性质,可以修改value。
场景1:商场无人值守车库,入口进场时扫描车牌,记录车牌和入场时间,出口离场时,扫描车牌,查找入场时间,用当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。

代码示例:
            
            
              cpp
              
              
            
          
          // 单词统计系统
BSTree<std::string, int> wordCount;
std::vector<std::string> words = {"apple", "banana", "apple", "orange", "banana", "apple"};
for (const auto& word : words) {
    auto node = wordCount.Find(word);
    if (node == nullptr) {
        wordCount.Insert(word, 1);
    } else {
        node->_value++;
    }
}
// 输出统计结果
// 中序遍历会按字典序输出六、总结
二叉搜索树作为一种基础而重要的数据结构,具有以下特点:
优点:
- 
动态性能好,支持高效插入删除 
- 
中序遍历可得有序序列 
- 
实现相对简单 
缺点:
- 
普通BST可能退化为链表 
- 
需要平衡机制保证性能 
适用场景:
- 
需要动态维护有序数据集 
- 
查找、插入、删除操作都较频繁 
- 
数据量不是特别大,或可以使用平衡BST 
掌握二叉搜索树不仅是学习更高级数据结构的基础,也是理解许多系统底层实现的关键。在实际应用中,我们通常使用其平衡变种(如红黑树)来保证稳定性能。