二叉搜索树(BST)详解
一、二叉搜索树的定义
根据笔记中的描述,二叉搜索树(又称排序二叉树)是一种具有严格排序特性的二叉树,其定义满足以下两种情况:
-
空树:即没有任何节点的树。
-
非空树:由根节点、左子树、右子树组成,且同时满足:
- 左子树和右子树都是二叉搜索树;
- 左子树中所有节点的值 小于 根节点的值;
- 右子树中所有节点的值 大于 根节点的值。
这种特性使得二叉搜索树的节点天然有序(左子树 < 根节点 < 右子树),为高效的查找、插入、删除操作奠定了基础。
二、二叉搜索树的核心操作(结合代码实现)
二叉搜索树的核心操作包括查找、插入、删除,以下结合提供的代码详细说明:
1. 查找操作
功能:查找数据域为特定值的节点。原理:利用 BST 的排序特性,通过与根节点比较,逐步缩小查找范围:
- 若目标值等于当前节点值,找到目标;
- 若目标值小于当前节点值,递归查找左子树;
- 若目标值大于当前节点值,递归查找右子树。
代码实现 :
function search(root, n) {
if (!root) { // 树为空或未找到目标,直接返回
return;
}
if (root.val === n) { // 找到目标节点
console.log('目标节点', root);
} else if (root.val > n) { // 目标在左子树
search(root.left, n);
} else { // 目标在右子树
search(root.right, n);
}
}
示例:在如下 BST 中查找值为 7 的节点:
6
/ \
3 8
/ \ / \
1 4 7 9
- 从根节点 6 开始,7 > 6 → 查找右子树(根为 8);
- 7 < 8 → 查找 8 的左子树,找到节点 7。
插入操作
功能:插入新节点并保持 BST 的排序特性。原理:类似查找逻辑,找到合适的位置(叶子节点的左 / 右子树)插入新节点:
- 若当前树为空,直接以新值创建根节点;
- 若新值小于当前节点值,递归向左子树插入;
- 若新值大于当前节点值,递归向右子树插入。
代码实现 :
function insertIntoBst(root, n) {
if (!root) { // 树为空,直接创建新节点作为根
root = new TreeNode(n);
return root;
}
if (root.val > n) { // 新值小,插入左子树
root.left = insertIntoBst(root.left, n);
} else { // 新值大,插入右子树
root.right = insertIntoBst(root.right, n);
}
return root; // 返回更新后的树
}
示例:向上述 BST 中插入值为 5 的节点:
- 5 < 6 → 左子树(根为 3);
- 5 > 3 → 右子树(根为 4);
- 5 > 4 → 插入 4 的右子树,最终 4 的右节点为 5。
3. 删除操作
功能:删除指定节点并保持 BST 的排序特性。原理:删除节点后需重新调整树结构,分三种情况:
- 叶子节点(无左右子树) :直接删除(置为 null);
- 有左子树:用左子树中最大值节点(左子树最右侧节点)替换当前节点,再删除该最大值节点;
- 有右子树(无左子树) :用右子树中最小值节点(右子树最左侧节点)替换当前节点,再删除该最小值节点。
代码实现 :
function deleteNode(root, n) {
if (!root) return root; // 树为空,直接返回
if (root.val === n) { // 找到目标节点
// 情况1:叶子节点
if (!root.left && !root.right) {
root = null;
}
// 情况2:有左子树(用左子树最大值替换)
else if (root.left) {
const maxLeft = findMax(root.left); // 找左子树最大值
root.val = maxLeft.val; // 替换值
root.left = deleteNode(root.left, maxLeft.val); // 删除最大值节点
}
// 情况3:有右子树(用右子树最小值替换)
else {
const minRight = findMin(root.right); // 找右子树最小值
root.val = minRight.val; // 替换值
root.right = deleteNode(root.right, minRight.val); // 删除最小值节点
}
}
// 目标在左子树
else if (root.val > n) {
root.left = deleteNode(root.left, n);
}
// 目标在右子树
else {
root.right = deleteNode(root.right, n);
}
return root;
}
// 辅助函数:找左子树最大值(最右侧节点)
function findMax(root) {
while (root.right) root = root.right;
return root;
}
// 辅助函数:找右子树最小值(最左侧节点)
function findMin(root) {
while (root.left) root = root.left;
return root;
}
示例:删除上述 BST 中值为 8 的节点:
- 8 有左右子树(7 和 9),且有右子树,因此用右子树最小值(9)替换 8;
- 替换后删除原 9 节点,最终 8 的位置变为 9,右子树为空。
三、时间复杂度
- 平均情况:查找、插入、删除的时间复杂度均为 O(log n) (类似二分查找,每次操作可排除一半节点)。
- 最坏情况:当树退化为斜树(所有节点只有左子树或右子树)时,时间复杂度为 O(n) (类似链表遍历)。
四、使用场景
二叉搜索树适合需要频繁进行查找、插入、删除操作,且对效率有一定要求的场景,例如:
- 数据库索引:利用 BST 的高效查找特性,快速定位数据(实际数据库中常用 B 树 / B + 树,是 BST 的变种,更适合磁盘存储)。
- 有序数据维护:如排行榜、字典等,可利用 BST 的有序性快速获取最大 / 最小值(如
findMax/findMin函数)。 - 平衡树基础:是 AVL 树、红黑树等平衡二叉树的基础(平衡树通过旋转解决 BST 的斜树问题,保证 O (log n) 的稳定效率)。