一、引言
在有序数据的查找、插入与删除操作中,普通线性表的效率往往难以满足高效处理需求。二叉查找树(Binary Search Tree,简称BST)作为一种基础且重要的树形数据结构,通过严格的大小约束规则,将动态集合的操作效率从线性级别提升至对数级别,成为计算机科学中实现有序映射、有序集合、索引结构的核心工具。本文将围绕 BST 树的定义、性质、操作、实现与应用,系统介绍这一经典数据结构。
二、BST 树的定义与性质
1. 基本定义
二叉查找树(BST)是一棵具有以下性质的二叉树:
- 若左子树非空,则左子树上所有节点的值均小于根节点的值;
- 若右子树非空,则右子树上所有节点的值均大于根节点的值;
- 左、右子树也分别是二叉查找树;
- 通常不允许键值重复(若允许需明确规则)。
2. 核心性质
- 中序遍历有序 :对 BST 进行中序遍历 ,可得到一个递增的有序序列,这是 BST 最关键的特征。
- 分层比较:任何节点与其左右孩子满足 "左小右大",查找路径可通过大小比较快速确定。
- 效率依赖树高 :理想情况下为平衡树,操作效率为O(log n) ;最坏情况退化为链表,效率为O(n)。
三、BST 树的基本结构
BST 的每个节点包含三部分信息:
- 关键字(数据域);
- 指向左孩子的指针;
- 指向右孩子的指针。
节点结构可表示为:
bash
+----------------+
| left | key | right |
+----------------+
四、BST 树的核心操作
BST 的三大基础操作为:查找、插入、删除,全部遵循 "左小右大" 规则。
1. 查找操作
- 从根节点开始:
- 若当前节点值 == 目标值 → 查找成功;
- 若目标值 < 当前节点值 → 进入左子树;
- 若目标值 > 当前节点值 → 进入右子树;
- 走到空节点 → 查找失败。
2. 插入操作
- 按照查找路径走到应插入的空位置;
- 创建新节点,连接到父节点的左或右指针;
- 插入过程不会改变已有结构,只新增叶子节点。
3. 删除操作(最复杂)
分三种情况:
- 删除叶子节点:直接删除,置空父节点指针。
- 删除仅有一个孩子的节点:用其子节点替代该节点。
- 删除有两个孩子的节点 :
- 找到前驱 (左子树最大值)或后继(右子树最小值);
- 用前驱 / 后继的值覆盖待删节点;
- 删除前驱 / 后继节点。
4. 遍历操作
- 前序遍历:根 → 左 → 右
- 中序遍历:左 → 根 → 右(得到递增序列)
- 后序遍历:左 → 右 → 根
- 层序遍历:按层次从上到下(借助队列)
五、BST 树的 C 语言实现
以下给出可直接运行的 BST 完整代码:
cpp
#include <stdio.h>
#include <stdlib.h>
// BST节点结构
typedef struct Node {
int key;
struct Node *left, *right;
} Node;
// 创建新节点
Node* createNode(int key) {
Node* node = (Node*)malloc(sizeof(Node));
node->key = key;
node->left = node->right = NULL;
return node;
}
// 插入节点
Node* insert(Node* root, int key) {
if (root == NULL) return createNode(key);
if (key < root->key)
root->left = insert(root->left, key);
else if (key > root->key)
root->right = insert(root->right, key);
return root;
}
// 查找节点
Node* search(Node* root, int key) {
if (root == NULL || root->key == key)
return root;
if (key < root->key)
return search(root->left, key);
return search(root->right, key);
}
// 找最小值节点(用于删除)
Node* minNode(Node* root) {
Node* cur = root;
while (cur && cur->left != NULL)
cur = cur->left;
return cur;
}
// 删除节点
Node* delete(Node* root, int key) {
if (root == NULL) return NULL;
if (key < root->key)
root->left = delete(root->left, key);
else if (key > root->key)
root->right = delete(root->right, key);
else {
// 1. 单/无子节点
if (root->left == NULL) {
Node* temp = root->right;
free(root);
return temp;
}
if (root->right == NULL) {
Node* temp = root->left;
free(root);
return temp;
}
// 2. 双孩子节点:用后继替换
Node* temp = minNode(root->right);
root->key = temp->key;
root->right = delete(root->right, temp->key);
}
return root;
}
// 中序遍历(输出有序序列)
void inOrder(Node* root) {
if (root == NULL) return;
inOrder(root->left);
printf("%d ", root->key);
inOrder(root->right);
}
int main() {
Node* root = NULL;
int arr[] = {50, 30, 70, 20, 40, 60, 80};
int n = sizeof(arr)/sizeof(arr[0]);
for (int i = 0; i < n; i++)
root = insert(root, arr[i]);
printf("中序遍历(递增):");
inOrder(root);
printf("\n");
root = delete(root, 50);
printf("删除50后:");
inOrder(root);
return 0;
}
六、BST 树的优缺点
优点
- 中序天然有序,无需额外排序;
- 动态操作高效:插入、删除、查找均无需移动大量元素;
- 实现简单,逻辑清晰,适合教学与基础工程使用。
缺点
- 容易退化:数据有序插入时退化成链表,效率变为 O (n);
- 无自平衡能力:需要 AVL、红黑树等改进结构保证效率。
七、BST 树的典型应用
- 有序集合与有序映射 :如 C++
set/map底层(红黑树); - 数据库索引:用于构建简单索引,加速范围查询;
- 排序算法:BST 排序等价于中序遍历,时间复杂度 O (n log n)~O (n²);
- 符号表:编译器、解释器用于管理变量名、函数名。
八、总结
二叉查找树(BST)是连接线性结构与平衡树形结构的关键桥梁。它基于 "左小右大" 的约束规则,实现了高效的动态有序操作,是理解 AVL 树、红黑树、B 树、B + 树的基础。尽管 BST 在极端情况下会退化,但其思想简洁、应用广泛,是数据结构中必须掌握的核心内容。
掌握 BST 的查找、插入、删除 三大操作与中序有序特性,就能真正理解动态查找结构的设计精髓,为后续学习更高级的平衡树与存储引擎打下坚实基础。