1. 二叉搜索树的基本定义
二叉搜索树(Binary Search Tree, BST)又称二叉排序树,是一种特殊的二叉树结构,满足以下性质:
- 若左子树不为空,则左子树上所有节点的值均 小于等于 根节点的值。
- 若右子树不为空,则右子树上所有节点的值均 大于等于 根节点的值。
- 左右子树本身也必须是二叉搜索树。
✅ 关键点:
- 支持重复值插入,但需保持逻辑一致性(如统一向右或向左插入相等元素)。
- 不支持修改键值(key),否则会破坏搜索树结构。
cpp
template <typename T>
struct TreeNode {
T val;
TreeNode* left;
TreeNode* right;
TreeNode(T x) : val(x), left(nullptr), right(nullptr) {}
};
2. 二叉搜索树的性能分析
| 情况 | 高度 | 时间复杂度 |
|---|---|---|
| 最优情况(完全/近似完全二叉树) | log2N\log_2 Nlog2N | O(logN)O(\log N)O(logN) |
| 最差情况(退化为链表) | NNN | O(N)O(N)O(N) |
⚠️ 综合来看,增删查改操作的时间复杂度为:O(N)O(N)O(N)
这在极端情况下效率极低,无法满足高性能需求。
🔍 解决方案 :引入平衡机制,形成 AVL树 和 红黑树 ,可保证高度始终为 O(logN)O(\log N)O(logN),从而将操作时间稳定在对数级别。
📌 补充说明:虽然二分查找也能达到 O(logN)O(\log N)O(logN) 效率,但它依赖于数组等支持随机访问的数据结构。其主要缺陷在于:
- 插入/删除需移动大量元素,效率低;
- 不适合动态数据集合。
3. 二叉搜索树的插入操作
插入过程遵循如下规则:
- 若树为空,创建新节点作为根节点。
- 否则从根开始比较:
- 插入值 < 当前节点值 → 往左子树走;
- 插入值 > 当前节点值 → 往右子树走;
- 插入值 == 当前节点值 → 可选择左或右(必须保持一致)。
- 找到空位置后,插入新节点。
cpp
template <typename T>
bool insert(TreeNode<T>*& root, const T& val) {
if (!root) {
root = new TreeNode<T>(val);
return true;
}
if (val <= root->val) {
return insert(root->left, val); // 允许相等值插入左子树
} else {
return insert(root->right, val); // 向右插入
}
}
💡 提示:若不允许重复值,可在
val == root->val时直接返回失败。
4. 二叉搜索树的查找操作
查找从根节点出发,根据大小关系决定路径:
- 目标值 < 当前节点值 → 向左查找;
- 目标值 > 当前节点值 → 向右查找;
- 目标值 == 当前节点值 → 找到目标;
- 到达空节点仍未找到 → 值不存在。
✅ 重要细节 :
若允许重复值存在,通常要求返回中序遍历中的第一个匹配节点(即最左侧的相同值)。
cpp
template <typename T>
TreeNode<T>* find(TreeNode<T>* root, const T& val) {
while (root) {
if (val < root->val) {
root = root->left;
} else if (val > root->val) {
root = root->right;
} else {
return root; // 找到相等值
}
}
return nullptr; // 未找到
}
🔄 递归版本也可实现:
cpp
template <typename T>
TreeNode<T>* find_recursive(TreeNode<T>* root, const T& val) {
if (!root || root->val == val) return root;
return val < root->val ? find_recursive(root->left, val)
: find_recursive(root->right, val);
}
5. 二叉搜索树的删除操作
删除操作需先确认节点存在,再按四种情况进行处理:
情况分类及处理方式:
| 情况 | 描述 | 处理方法 |
|---|---|---|
| ① 叶子节点(左右子树为空) | 删除该节点即可 | 直接释放内存 |
| ② 只有右子树 | 用右子树替代当前节点 | 父亲指针指向右孩子 |
| ③ 只有左子树 | 用左子树替代当前节点 | 父亲指针指向左孩子 |
| ④ 两个子树都存在 | 无法直接删除,使用"替换法" | 替换为左子树最大值或右子树最小值 |
✅ 推荐策略:
使用 右子树的最小值节点(最左节点) 替代被删节点,更利于维护平衡。
cpp
template <typename T>
bool remove(TreeNode<T>*& root, const T& val) {
if (!root) return false;
if (val < root->val) {
return remove(root->left, val);
} else if (val > root->val) {
return remove(root->right, val);
} else {
// 找到要删除的节点
if (!root->left && !root->right) {
delete root;
root = nullptr;
} else if (!root->left) {
TreeNode<T>* temp = root;
root = root->right;
delete temp;
} else if (!root->right) {
TreeNode<T>* temp = root;
root = root->left;
delete temp;
} else {
// 有两个子节点:找右子树中最左边的节点(最小值)
TreeNode<T>* successor = root->right;
while (successor->left) {
successor = successor->left;
}
// 交换值
std::swap(root->val, successor->val);
// 删除原位置的节点(此时 successor 是叶子或只有一个子树)
remove(root->right, val);
}
return true;
}
}
✅ 优点:通过值交换,将复杂删除转化为简单情况。
6. 二叉搜索树的完整代码实现(基于 C++)
cpp
//二叉搜索树
#include <iostream>
using namespace std;
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>
struct 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;
}
//中序
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool Find(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
{
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 (cur != _root)
{
// 左为空,父亲指向我的右
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
else
{
_root = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur != _root)
{
// 右为空,父亲指向我的左
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
else
{
_root = cur->_left;
}
delete cur;
}
else
{
// 左右都不为空,找子树中适合的节点替代我
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
if (minRight == minRightParent->_left)
minRightParent->_left = minRight->_right;
else
minRightParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
✅ 使用示例:
cpp
int main() {
BinarySearchTree<int> bst;
int arr[] = {50, 30, 70, 20, 40, 60, 80, 30}; // 包含重复值
for (int x : arr) {
bst.insert(x);
}
std::cout << "中序遍历结果:";
bst.inorderTraversal(); // 输出: 20 30 30 40 50 60 70 80
auto node = bst.find(40);
if (node) {
std::cout << "找到值 40" << std::endl;
}
bst.remove(30); // 仅删除一个 30(中序第一个)
std::cout << "删除 30 后中序遍历:";
bst.inorderTraversal();
return 0;
}
7. 二叉搜索树的应用场景详解
7.1 仅 Key 搜索场景
- 用途:判断某个键是否存在。
- 结构特点 :每个节点只存储
key。 - 典型应用 :
- 唯一性校验(如用户ID去重)
- 集合类容器(如
std::set)
- 限制 :
- 不支持修改键;
- 不能存储附加信息。
cpp
// 简化版:仅存储 key
struct SetNode {
int key;
SetNode* left, *right;
SetNode(int k) : key(k), left(nullptr), right(nullptr) {}
};
7.2 Key/Value 映射场景
- 用途:以 key 查找对应的 value。
- 结构特点 :每个节点包含
key与value。 - 典型应用 :
- 缓存系统(LRU Cache)
- 数据库索引
- 字典、映射表(如
std::map)
- 优势 :
- 快速查找、插入、删除;
- 支持修改
value; - 保持有序性(中序遍历即有序)。
cpp
template <typename K, typename V>
struct KVNode {
K key;
V value;
KVNode* left;
KVNode* right;
KVNode(K k, V v) : key(k), value(v), left(nullptr), right(nullptr) {}
};
✅ 代码实现要点:
- 所有操作仍以
key为比较依据;- 返回的是
KVNode*,可通过->value获取对应值;- 修改
value安全,修改key会破坏树结构,应避免。
7.3 Key/Value 二叉搜索树完整实现(模板版)
cpp
template <typename K, typename V>
class MapBST {
private:
struct Node {
K key;
V value;
Node* left;
Node* right;
Node(K k, V v) : key(k), value(v), left(nullptr), right(nullptr) {}
};
Node* root;
void destroy(Node* node) {
if (!node) return;
destroy(node->left);
destroy(node->right);
delete node;
}
public:
MapBST() : root(nullptr) {}
~MapBST() {
destroy(root);
}
void insert(K key, V value) {
insertHelper(root, key, value);
}
bool get(K key, V& out_value) const {
Node* node = find(root, key);
if (node) {
out_value = node->value;
return true;
}
return false;
}
void update(K key, V new_value) {
Node* node = find(root, key);
if (node) {
node->value = new_value;
}
}
bool remove(K key) {
return removeHelper(root, key);
}
void inorder() const {
inorderHelper(root);
std::cout << std::endl;
}
private:
void insertHelper(Node*& node, K key, V value) {
if (!node) {
node = new Node(key, value);
return;
}
if (key < node->key) {
insertHelper(node->left, key, value);
} else {
insertHelper(node->right, key, value);
}
}
Node* find(Node* node, K key) const {
while (node) {
if (key == node->key) return node;
if (key < node->key) node = node->left;
else node = node->right;
}
return nullptr;
}
bool removeHelper(Node*& node, K key) {
if (!node) return false;
if (key < node->key) {
return removeHelper(node->left, key);
} else if (key > node->key) {
return removeHelper(node->right, key);
} else {
if (!node->left && !node->right) {
delete node;
node = nullptr;
} else if (!node->left) {
Node* temp = node;
node = node->right;
delete temp;
} else if (!node->right) {
Node* temp = node;
node = node->left;
delete temp;
} else {
Node* succ = node->right;
while (succ->left) succ = succ->left;
std::swap(node->key, succ->key);
std::swap(node->value, succ->value);
removeHelper(node->right, key);
}
return true;
}
}
void inorderHelper(Node* node) const {
if (!node) return;
inorderHelper(node->left);
std::cout << "{" << node->key << ": " << node->value << "} ";
inorderHelper(node->right);
}
};
✅ 示例使用:
cpp
int main() {
MapBST<std::string, int> phoneBook;
phoneBook.insert("Alice", 123456);
phoneBook.insert("Bob", 789012);
phoneBook.insert("Charlie", 345678);
int num;
if (phoneBook.get("Alice", num)) {
std::cout << "Alice's number: " << num << std::endl;
}
phoneBook.update("Bob", 999999);
std::cout << "更新后:";
phoneBook.inorder(); // {Alice: 123456} {Bob: 999999} {Charlie: 345678}
return 0;
}
总结:核心要点提炼
| 特性 | 说明 |
|---|---|
| ✅ 结构特性 | 左 ≤ 根 ≤ 右,递归定义 |
| ✅ 插入 | 从根出发,按大小方向走,直到空位插入 |
| ✅ 查找 | 逐层比较,最多走高度次 |
| ✅ 删除 | 四种情况,双子树用"替换法" |
| ✅ 时间复杂度 | 最好 O(logN)O(\log N)O(logN),最坏 O(N)O(N)O(N) |
| ✅ 应用场景 | 仅 key → 集合;key/value → 映射表 |
| ✅ 限制 | 不支持修改 key,否则破坏结构 |
| ✅ 升级方案 | 用 AVL / 红黑树解决不平衡问题 |
🚀 最终建议 :
在实际工程中,不要直接使用原始二叉搜索树 。
应优先选用标准库中的
std::set(仅 key)、std::map(key/value),它们底层正是基于 红黑树 实现,具备良好的性能和稳定性。