一、BST 基本概念
二叉搜索树(Binary Search Tree,BST)是一种有序二叉树,核心性质:
-
若左子树不为空,左子树所有节点值 小于 根节点值
-
若右子树不为空,右子树所有节点值 大于 根节点值
-
左右子树也都是二叉搜索树
-
无重复节点值
中序遍历 BST 会得到升序序列,这是 BST 最核心的特性。
二、BST 节点结构
template<class K>
struct BSTreeNode {
BSTreeNode<K>* _left; // 左孩子指针
BSTreeNode<K>* _right; // 右孩子指针
K _key; // 节点值
// 构造函数:初始化节点,左右孩子默认为空
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
-
节点包含三个成员:左指针、右指针、节点值
-
构造函数用于创建新节点,自动初始化指针为空,避免野指针
三、BST 类核心:四大默认成员函数
包含默认构造、拷贝构造、赋值重载、析构函数,统一管理树的创建、拷贝、销毁。
template<class K>
class BSTree {
typedef BSTreeNode<K> Node;
public:
// 1. 默认构造函数:初始化空树
BSTree()
:_root(nullptr)
{}
// 2. 拷贝构造函数:深拷贝整棵树
BSTree(const BSTree<K>& t) {
_root = Copy(t._root);
}
// 3. 析构函数:释放所有节点内存
~BSTree() {
Destroy(_root);
}
// 4. 赋值运算符重载:现代写法(交换指针,简洁安全)
BSTree<K>& operator=(BSTree<K> t) {
swap(_root, t._root);
return *this;
}
private:
// 递归拷贝树(深拷贝)
Node* Copy(Node* root) {
if (root == nullptr) return nullptr;
Node* copyroot = new Node(root->_key); // 拷贝当前节点
copyroot->_left = Copy(root->_left); // 递归拷贝左子树
copyroot->_right = Copy(root->_right); // 递归拷贝右子树
return copyroot;
}
// 递归销毁树(释放内存+置空指针)
void Destroy(Node*& root) {
if (root == nullptr) return;
Destroy(root->_left); // 销毁左子树
Destroy(root->_right); // 销毁右子树
delete root; // 释放当前节点
root = nullptr; // 指针置空,防止野指针
}
Node* _root; // 树根节点
};
核心细节解析
-
深拷贝:拷贝构造不能直接拷贝指针,必须递归创建新节点,避免多个对象共用同一块内存
-
赋值重载现代写法:传值参数会自动调用拷贝构造生成临时对象,交换指针后,临时对象销毁自动释放原内存,简洁且异常安全
-
Destroy 中的引用
Node*& root:-
作用:不仅能释放节点内存,还能修改实参指针本身,将指针置空
-
意义:防止函数结束后,原指针变成野指针,这是内存管理的关键细节
-
四、BST 核心功能(递归 + 非递归 对照)
1. 查找功能
作用:判断树中是否存在目标值,利用 BST 有序性,每次比较排除一半子树。
// 非递归查找
bool Find(const K& key) {
Node* cur = _root;
while (cur) {
if (cur->_key < key) // 目标值更大,去右子树
cur = cur->_right;
else if (cur->_key > key) // 目标值更小,去左子树
cur = cur->_left;
else // 找到目标
return true;
}
return false; // 遍历到空,未找到
}
// 递归查找
bool FindR(const K& key) {
return _FindR(_root, key);
}
// 递归辅助函数
bool _FindR(Node* root, const K& key) {
if (root == nullptr) return false; // 递归终止:空树
if (root->_key < key)
return _FindR(root->_right, key);
else if (root->_key > key)
return _FindR(root->_left, key);
else
return true; // 找到目标
}
2. 插入功能
作用:向树中添加新节点,保证 BST 性质,不允许重复值。
// 非递归插入
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;
}
// 递归插入
bool InsertR(const K& key) {
return _InsertR(_root, key);
}
// 递归辅助函数(核心:Node*& root)
bool _InsertR(Node*& root, const K& key) {
if (root == nullptr) {
root = new Node(key); // 空位置直接创建节点
return true;
}
if (root->_key < key)
return _InsertR(root->_right, key);
else if (root->_key > key)
return _InsertR(root->_left, key);
else
return false; // 重复值
}
关键语法:_InsertR 中 Node*& root 的妙用
-
递归函数参数是指针的引用,直接操作上层节点的指针(根节点/左/右孩子指针)
-
无需单独记录父节点,找到空位置时,直接修改原指针指向新节点
-
代码极简,逻辑清晰,完美适配递归场景,是递归实现 BST 的点睛之笔
3. 删除功能
删除分三种情况:
-
待删除节点左子树为空:父节点直接指向其右孩子
-
待删除节点右子树为空:父节点直接指向其左孩子
-
待删除节点左右子树都不为空:替换法(用左子树最大值/右子树最小值替换当前节点,再删除替换节点)
// 非递归删除
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 { // 找到目标节点
// 情况1:左子树为空
if (cur->_left == nullptr) {
if (cur == _root)
_root = cur->_right;
else
parent->_right == cur ? parent->_right = cur->_right : parent->_left = cur->_right;
delete cur;
}
// 情况2:右子树为空
else if (cur->_right == nullptr) {
if (cur == _root)
_root = cur->_left;
else
parent->_right == cur ? parent->_right = cur->_left : parent->_left = cur->_left;
delete cur;
}
// 情况3:左右子树都不为空(替换法:找右子树最小值)
else {
Node* minParent = cur;
Node* minRight = cur->_right;
// 找到右子树最左节点(最小值)
while (minRight->_left) {
minParent = minRight;
minRight = minRight->_left;
}
// 替换值
cur->_key = minRight->_key;
// 删除替换节点
minParent->_left == minRight ? minParent->_left = minRight->_right : minParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false; // 未找到目标值
}
// 递归删除
bool EraseR(const K& key) {
return _EraseR(_root, key);
}
// 递归辅助函数(核心:Node*& root)
bool _EraseR(Node*& root, const K& key) {
if (root == nullptr) return false;
// 查找目标节点
if (root->_key < key)
return _EraseR(root->_right, key);
else if (root->_key > key)
return _EraseR(root->_left, key);
// 找到待删除节点
else {
Node* del = root;
// 情况1+2:左空/右空,直接修改指针指向孩子
if (root->_left == nullptr)
root = root->_right;
else if (root->_right == nullptr)
root = root->_left;
// 情况3:左右都不为空,替换法
else {
// 找左子树最大值(最右节点)
Node* leftMax = root->_left;
while (leftMax->_right)
leftMax = leftMax->_right;
// 交换值
swap(root->_key, leftMax->_key);
// 递归删除替换后的节点
return _EraseR(root->_left, root->_key);
}
delete del;
return true;
}
}
递归删除 Node*& root 核心作用
-
直接修改上层节点的指针,无需记录父节点,简化逻辑
-
根节点删除、普通节点删除逻辑统一,无需单独判断根节点
-
替换法递归删除时,自动处理指针链接,代码极度简洁
五、中序遍历
验证 BST 有序性,递归实现:
void InOrder() {
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root) {
if (root == NULL) return;
_InOrder(root->_left); // 左
cout << root->_key << " "; // 根
_InOrder(root->_right); // 右
}
六、关键知识点总结
-
指针引用
Node*&的核心价值-
递归插入/删除/销毁中使用,直接修改上层指针本身
-
省略父节点记录,简化代码逻辑
-
确保指针能被置空,避免野指针
-
-
递归 vs 非递归
-
非递归:效率高,无栈溢出风险,逻辑繁琐(需记录父节点)
-
递归:代码简洁、可读性高,依赖
Node*&实现指针修改,深度过大可能栈溢出
-
-
BST 核心操作时间复杂度
-
最优(平衡树):O(\\log N)
-
最坏(退化为链表):O(N)
-
-
删除核心
- 左右子树都不为空时,必须用替换法,保证 BST 性质不变