BST

二叉搜索树(Binary Search Tree,简称 BST),也常被称为二叉查找树或二叉排序树。它是二叉树中最常用的一种变体,专门为快速查找、插入和删除数据而设计。


1. BST 的核心性质

一棵 BST 必须满足以下三个性质:

  1. 左子树上所有结点的值均小于它的根结点的值。
  2. 右子树上所有结点的值均大于它的根结点的值。
  3. 左、右子树也分别为二叉搜索树。
  4. (通常情况下)树中不包含重复的节点。

重要结论: 对一棵 BST 进行中序遍历(Left -> Root -> Right),得到的结果一定是一个升序排列的序列。这是检验一棵树是否为 BST 的最直观方法。


2. 核心操作详解

从根节点开始:

  • 如果目标值等于当前节点值,查找成功。
  • 如果目标值小于当前节点值,则进入左子树继续查找。
  • 如果目标值大于当前节点值,则进入右子树继续查找。
  • 如果走到空节点,说明目标值不存在。

B. 插入 (Insertion)

插入操作总是从根节点开始,逻辑与查找类似。新节点最终会被插入到某个叶子节点的位置,从而保持 BST 的性质。

C. 删除 (Deletion) —— 最复杂的操作

删除节点需要分三种情况讨论,以确保删除后树依然满足 BST 性质:

  1. 被删节点是叶子节点: 直接删除,将其父节点指向它的指针置为空。
  2. 被删节点只有一个孩子: 将该节点的父节点直接指向它的那个孩子(类似链表删除)。
  3. 被删节点有两个孩子: * 找到该节点的中序后继(右子树中最小的节点)或中序前驱(左子树中最大的节点)。
    • 用后继(或前驱)节点的值替换掉要删除节点的值。
    • 最后删除那个多余的后继(或前驱)节点。

3. 性能分析

BST 的效率极大地依赖于树的高度 ($h$)

操作 平均时间复杂度 最坏时间复杂度 (退化为链表)
查找 $O(\log n)$ $O(n)$
插入 $O(\log n)$ $O(n)$
删除 $O(\log n)$ $O(n)$
  • 理想状态: 树是平衡的(接近满二叉树),高度 $h = \log n$。
  • 最坏状态: 插入的数据是有序的(如 1, 2, 3, 4, 5),树会退化成一条斜树(链表),此时查找效率降为 $O(n)$。

4. 代码实现

#include <iostream>
#include <algorithm>

using namespace std;

// 定义节点结构
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class BST {
public:
    BST() : root(nullptr) {}

    // 插入接口
    void insert(int val) {
        root = insert(root, val);
    }

    // 查找接口
    bool search(int val) {
        return search(root, val) != nullptr;
    }

    // 删除接口
    void remove(int val) {
        root = remove(root, val);
    }

    // 中序遍历(打印排序结果)
    void inorder() {
        inorder(root);
        cout << endl;
    }

private:
    TreeNode* root;

    // 递归插入
    TreeNode* insert(TreeNode* node, int val) {
        if (node == nullptr) return new TreeNode(val);
        
        if (val < node->val)
            node->left = insert(node->left, val);
        else if (val > node->val)
            node->right = insert(node->right, val);
            
        return node;
    }

    // 递归查找
    TreeNode* search(TreeNode* node, int val) {
        if (node == nullptr || node->val == val) return node;
        
        if (val < node->val) return search(node->left, val);
        return search(node->right, val);
    }

    // 查找最小值节点(删除操作辅助函数)
    TreeNode* findMin(TreeNode* node) {
        while (node && node->left) node = node->left;
        return node;
    }

    // 递归删除(最核心部分)
    TreeNode* remove(TreeNode* node, int val) {
        if (node == nullptr) return nullptr;

        if (val < node->val) {
            node->left = remove(node->left, val);
        } else if (val > node->val) {
            node->right = remove(node->right, val);
        } else {
            // 找到目标节点了!
            
            // 情况 1 & 2: 叶子节点或只有一个孩子
            if (node->left == nullptr) {
                TreeNode* temp = node->right;
                delete node;
                return temp;
            } else if (node->right == nullptr) {
                TreeNode* temp = node->left;
                delete node;
                return temp;
            }

            // 情况 3: 有两个孩子
            // 找到右子树中最小的节点(中序后继)
            TreeNode* temp = findMin(node->right);
            // 替换值
            node->val = temp->val;
            // 删除掉右子树中那个多余的后继节点
            node->right = remove(node->right, temp->val);
        }
        return node;
    }

    // 递归中序遍历
    void inorder(TreeNode* node) {
        if (node == nullptr) return;
        inorder(node->left);
        cout << node->val << " ";
        inorder(node->right);
    }
};

// 测试代码
int main() {
    BST tree;
    
    // 插入测试
    int nums[] = {50, 30, 20, 40, 70, 60, 80};
    for (int x : nums) tree.insert(x);

    cout << "中序遍历结果(应为升序): ";
    tree.inorder();

    // 查找测试
    cout << "查找 40: " << (tree.search(40) ? "存在" : "不存在") << endl;
    cout << "查找 90: " << (tree.search(90) ? "存在" : "不存在") << endl;

    // 删除测试
    cout << "删除 20 (叶子节点)..." << endl;
    tree.remove(20);
    tree.inorder();

    cout << "删除 30 (有一个孩子)..." << endl;
    tree.remove(30);
    tree.inorder();

    cout << "删除 50 (有两个孩子,根节点)..." << endl;
    tree.remove(50);
    tree.inorder();

    return 0;
}

5. 进阶:如何解决“退化”问题?

由于普通 BST 在极端情况下会退化为链表,为了保证 $O(\log n)$ 的性能,工程师们开发了自平衡二叉搜索树

  • AVL 树: 严格平衡,任何节点的左右子树高度差不超过 1。
  • 红黑树 (Red-Black Tree): 弱平衡,但在插入和删除时旋转次数较少。它是 Java 的 TreeMapTreeSet 以及 C++ STL 中 map 的底层实现。
posted @ 2025-12-20 20:51  belief73  阅读(2)  评论(0)    收藏  举报