二叉搜索树(BST)详解:原理、操作与实现

179 阅读4分钟

二叉搜索树(BST)详解:原理、操作与实现

在计算机科学中,二叉搜索树(Binary Search Tree,简称 BST)是一种非常重要的数据结构,它在保持数据有序的同时,支持高效的查找、插入和删除操作。本文将深入讲解二叉搜索树的定义、性质、基本操作及其 JavaScript 实现。


一、什么是二叉搜索树?

二叉搜索树(BST)是一种特殊的二叉树,满足以下递归定义:

  • 它是一棵空树;

  • 它由一个根节点、一棵左子树和一棵右子树组成,且:

    • 左子树中所有节点的值 严格小于 根节点的值;
    • 右子树中所有节点的值 严格大于 根节点的值;
    • 左子树和右子树本身也都是二叉搜索树。

注意:通常约定 BST 中不允许重复值。若需支持重复,可约定“左 ≤ 根 < 右”等变体,但标准定义要求严格大小关系。

这种结构性质使得 BST 具备天然的有序性,从而支持高效的搜索操作。


二、时间复杂度分析

操作平均时间复杂度最坏时间复杂度
查找O(log n)O(n)
插入O(log n)O(n)
删除O(log n)O(n)
  • 平均情况:当树接近“平衡”(如完全二叉树)时,高度约为 log n,操作效率高。
  • 最坏情况:当插入的数据已排序(如 1,2,3,4,5),BST 退化为链表,高度为 n,性能急剧下降。

因此,在实际应用中常使用自平衡二叉搜索树(如 AVL 树、红黑树)来避免退化。


三、核心操作实现(JavaScript)

1. 节点定义

javascript
编辑
class TreeNode {
    constructor(val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}

2. 查找(Search)

从根开始,根据目标值与当前节点值的大小关系,决定向左或向右递归。

javascript
编辑
function search(root, target) {
    if (!root) return null; // 未找到

    if (root.val === target) {
        return root;
    } else if (root.val > target) {
        return search(root.left, target);
    } else {
        return search(root.right, target);
    }
}

示例:search(root, 7) 将返回值为 7 的节点。


3. 插入(Insert)

插入新节点时,始终将其作为叶子节点插入,以维持 BST 性质。

javascript
编辑
function insertIntoBST(root, val) {
    if (!root) {
        return new TreeNode(val); // 创建新节点
    }

    if (val < root.val) {
        root.left = insertIntoBST(root.left, val);
    } else {
        root.right = insertIntoBST(root.right, val);
    }

    return root;
}

注意:该函数返回新的根节点(在递归回溯中重建引用)。


4. 删除(Delete)

删除是 BST 中最复杂的操作,需分三种情况处理:

情况 1:待删节点是叶子节点
  • 直接删除,返回 null
情况 2:待删节点只有一个子树
  • 用其唯一子节点替代它。
情况 3:待删节点有两个子树
  • 找到左子树的最大值(前驱)或右子树的最小值(后继);
  • 用该值替换当前节点的值;
  • 然后递归删除那个前驱或后继节点(它最多只有一个子树)。
javascript
编辑
function deleteNode(root, key) {
    if (!root) return null;

    if (key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        // 找到要删除的节点
        if (!root.left && !root.right) {
            return null; // 情况1:叶子节点
        } else if (root.left) {
            // 情况3:优先用左子树最大值(前驱)
            const maxLeft = findMax(root.left);
            root.val = maxLeft.val;
            root.left = deleteNode(root.left, maxLeft.val);
        } else {
            // 情况2 或 情况3(无左子树,用右子树最小值)
            const minRight = findMin(root.right);
            root.val = minRight.val;
            root.right = deleteNode(root.right, minRight.val);
        }
    }
    return root;
}

function findMax(node) {
    while (node.right) node = node.right;
    return node;
}

function findMin(node) {
    while (node.left) node = node.left;
    return node;
}

注:上述代码中有一个笔误——root.rigth 应为 root.right,已在修正版本中更正。


四、示例树结构

构建如下 BST:

text
编辑
        6
      /   \
     3     8
    / \   / \
   1   4 7   9
  • 查找 7:路径为 6 → 8 → 7,成功找到。
  • 插入 5:成为 4 的右孩子。
  • 删除 3:用左子树最大值 1 或右子树最小值 4 替代(此处用 4),然后删除原 4 节点。

五、总结

二叉搜索树是一种兼具有序性高效操作的数据结构,广泛应用于数据库索引、集合实现、符号表等场景。尽管其最坏性能不佳,但通过引入平衡机制(如 AVL、红黑树),可保证 O(log n) 的稳定性能。

掌握 BST 的三大操作(查找、插入、删除)是理解更高级树结构的基础。建议动手实现并调试上述代码,加深对递归与指针操作的理解。