二叉搜索树(BST)是数据结构中排序、查找、动态增删的核心结构,也是STL中map/set、multimap/multiset的底层实现基础。本文把课件里的全部知识点完整梳理,从定义到性能、四大操作、完整代码、两大应用场景

一、二叉搜索树(BST)核心定义

二叉搜索树又称二叉排序树,满足以下严格性质:

  1. 左子树不为空 → 左子树上所有节点值 ≤ 根节点值
  2. 右子树不为空 → 右子树上所有节点值 ≥ 根节点值
  3. 左右子树本身也必须是二叉搜索树

关于重复值的规则

  • 支持重复值:插入时统一向左/向右,保持逻辑一致
  • 不支持重复值:遇到相等直接返回插入失败
  • 对应STL:
    • map/set:不支持重复key
    • multimap/multiset:支持重复key

二、二叉搜索树性能分析(必考点)

BST的效率完全由树的高度决定:

  1. 最优情况:完全二叉树/接近完全二叉树
    高度 = log₂N,增删查改时间复杂度 O(logN)
  2. 最差情况:退化成单支树(链表)
    高度 = N,增删查改时间复杂度 O(N)

为什么不用二分查找替代?

二分查找虽然也是O(logN),但有致命缺陷:

  1. 必须放在支持下标随机访问的有序结构(数组)
  2. 插入/删除需要大量挪动数据,效率极低

因此:二叉搜索树适合动态数据,二分查找适合静态有序数据
为了解决BST退化问题,后续会学习AVL树、红黑树(平衡二叉搜索树)。


三、二叉搜索树四大核心操作

1. 插入操作

步骤:

  1. 树为空 → 新建节点直接作为根节点
  2. 树不为空:
    • 插入值 > 当前节点 → 向右走
    • 插入值 < 当前节点 → 向左走
  3. 找到空位置,插入新节点
  4. 重复值:统一左/右插入,不可左右混用

2. 查找操作

步骤:

  1. 从根节点开始比较
  2. 目标值 > 当前节点 → 右子树查找
  3. 目标值 < 当前节点 → 左子树查找
  4. 走到空节点 → 说明不存在
  5. 不支持重复值:找到即返回
  6. 支持重复值:返回中序第一个匹配节点

3. 删除操作(最难,分4种情况)

设待删除节点为N,先查找是否存在,不存在直接返回false。

情况1:N左右孩子都为空

  • 父节点对应孩子指针置空,直接删除N

情况2:N左孩子为空,右孩子不为空

  • 父节点指向N的右孩子,删除N

情况3:N右孩子为空,左孩子不为空

  • 父节点指向N的左孩子,删除N

情况4:N左右孩子都不为空(核心难点)

  • 无法直接删除,使用替换法
  • 选择两个替换节点之一:
    1. N的左子树最大值(最右节点)
    2. N的右子树最小值(最左节点)
  • 交换N与替换节点的值
  • 此时问题转化为删除替换节点(必属于情况1/2/3)

4. 中序遍历

二叉搜索树中序遍历结果一定是升序序列,这是判断BST的重要依据。


四、C++ 完整代码实现

1. 纯Key版二叉搜索树

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;
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
            {
                // 左孩子为空
                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;
                }
                // 右孩子为空
                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;
                }
                // 左右孩子都不为空,替换法
                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);
    }

private:
    Node* _root = nullptr;
};

2. Key/Value版二叉搜索树(键值对)

支持key映射value,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, const V& value)
        :_key(key)
        , _value(value)
        , _left(nullptr)
        , _right(nullptr)
    {}
};

template<class K, class V>
class BSTree
{
    typedef BSTNode<K, V> Node;
public:
    BSTree() = default;

    BSTree(const BSTree<K, V>& t)
    {
        _root = Copy(t._root);
    }

    BSTree<K, V>& operator=(BSTree<K, V> t)
    {
        swap(_root, t._root);
        return *this;
    }

    ~BSTree()
    {
        Destroy(_root);
        _root = nullptr;
    }

    // 插入键值对
    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;
    }

    // 查找,返回节点指针
    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;
    }

    // 删除
    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)
                {
                    if (parent == nullptr)
                        _root = cur->_right;
                    else
                    {
                        if (parent->_left == cur)
                            parent->_left = cur->_right;
                        else
                            parent->_right = cur->_right;
                    }
                    delete cur;
                    return true;
                }
                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;
                }
                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 << ":" << root->_value << endl;
        _InOrder(root->_right);
    }

    void Destroy(Node* root)
    {
        if (root == nullptr) return;
        Destroy(root->_left);
        Destroy(root->_right);
        delete 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;
    }

private:
    Node* _root = nullptr;
};

五、二叉搜索树两大使用场景

场景1:纯Key模型(只判断存在与否)

只存key,用于存在性检查,不支持修改key。
典型例子:

  1. 小区车库:车牌白名单校验,在则放行
  2. 单词拼写检查:文章单词与词库比对,不存在标红

场景2:Key/Value模型(键值映射)

每个key对应一个value,按key查找,可修改value,不可修改key。
典型例子:

  1. 中英字典:英文key → 中文value
  2. 商场停车:车牌key → 入场时间value,计费使用
  3. 词频统计:单词key → 出现次数value

场景测试代码

  1. 字典示例
int main()
{
    BSTree<string, string> dict;
    dict.Insert("left", "左边");
    dict.Insert("right", "右边");
    dict.Insert("insert", "插入");
    dict.Insert("string", "字符串");

    string str;
    while (cin>>str)
    {
        auto ret = dict.Find(str);
        if (ret)
            cout << "->" << ret->_value << endl;
        else
            cout << "无此单词,请重新输入" << endl;
    }
    return 0;
}
  1. 词频统计示例
int main()
{
    string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", 
                     "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
    BSTree<string, int> countTree;

    for (const auto& str : arr)
    {
        auto ret = countTree.Find(str);
        if (ret == nullptr)
            countTree.Insert(str, 1);
        else
            ret->_value++;
    }

    countTree.InOrder();
    return 0;
}

六、全文总结

  1. 二叉搜索树核心:左小右大,中序有序
  2. 效率:最优O(logN),最坏O(N),退化是最大问题
  3. 操作难点:删除(替换法)
  4. 两种模型:
    • 纯key:存在性检查
    • key/value:键值映射、统计、索引
  5. 地位:STL关联容器底层基础,进阶平衡二叉树的前提
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐