- 二叉搜索树的高级操作
二叉搜索树的核心在于其有序性:左子树所有节点值 < 根节点值 < 右子树所有节点值。
- 插入与删除的递归/迭代实现:
- 递归实现简洁,但深度过大时可能导致栈溢出。
- 迭代实现更安全,需仔细处理指针的重新链接。
cpp
// 迭代方式删除节点
template <typename T>
TreeNode<T>* BST<T>::deleteNode(TreeNode<T>* root, T key) {
TreeNode<T>* curr = root;
TreeNode<T>* parent = nullptr;
// 查找待删除节点及其父节点
while (curr != nullptr && curr->data != key) {
parent = curr;
if (key < curr->data) curr = curr->left;
else curr = curr->right;
}
if (curr == nullptr) return root; // 未找到
// Case 1: 无子节点或仅有一个子节点
if (curr->left == nullptr || curr->right == nullptr) {
TreeNode<T>* newChild = (curr->left != nullptr) ? curr->left : curr->right;
if (parent == nullptr) return newChild; // 删除的是根节点
if (parent->left == curr) parent->left = newChild;
else parent->right = newChild;
delete curr;
}
// Case 2: 有两个子节点 - 找后继节点
else {
TreeNode<T>* successor = curr->right;
TreeNode<T>* succParent = curr;
while (successor->left != nullptr) {
succParent = successor;
successor = successor->left;
}
curr->data = successor->data; // 复制后继值
// 删除后继节点 (后继最多只有一个右子节点)
if (succParent->left == successor) succParent->left = successor->right;
else succParent->right = successor->right;
delete successor;
}
return root;
}
2. 平衡二叉树
普通BST在插入顺序不当时会退化成链表,时间复杂度恶化到O(n)。平衡二叉树通过旋转操作保持树高度大致平衡,确保操作在O(\\log n)时间内完成。
- AVL树: 严格的平衡条件,每个节点的左右子树高度差绝对值不超过1。通过四种旋转(LL, RR, LR, RL)恢复平衡。
cpp
// AVL树节点结构
template <typename T>
struct AVLNode {
T data;
AVLNode* left;
AVLNode* right;
int height; // 节点高度
AVLNode(T val) : data(val), left(nullptr), right(nullptr), height(1) {}
};
// 计算高度
template <typename T>
int height(AVLNode<T>* node) {
if (node == nullptr) return 0;
return node->height;
}
// 右旋 (处理左左情况)
template <typename T>
AVLNode<T>* rightRotate(AVLNode<T>* y) {
AVLNode<T>* x = y->left;
AVLNode<T>* T2 = x->right;
// 旋转
x->right = y;
y->left = T2;
// 更新高度
y->height = 1 + std::max(height(y->left), height(y->right));
x->height = 1 + std::max(height(x->left), height(x->right));
return x; // 新的根节点
}
// 类似地实现左旋和其他旋转...
- 红黑树: 应用更广泛(如C++ STL中的
std::map,std::set)。平衡条件略宽松,通过节点颜色(红/黑)和规则保证最长路径不超过最短路径的两倍。插入和删除时需调整颜色和旋转。
3. 高级遍历与应用
- Morris遍历: 空间复杂度O(1)的中序遍历。通过临时修改指针实现,无需栈或递归。
cpp
template <typename T>
void morrisInorder(TreeNode<T>* root) {
TreeNode<T>* curr = root;
while (curr != nullptr) {
if (curr->left == nullptr) {
std::cout << curr->data << " "; // 访问
curr = curr->right;
} else {
// 找到前驱节点
TreeNode<T>* pre = curr->left;
while (pre->right != nullptr && pre->right != curr) {
pre = pre->right;
}
if (pre->right == nullptr) {
pre->right = curr; // 建立临时链接
curr = curr->left;
} else { // pre->right == curr, 已访问过左子树
pre->right = nullptr; // 恢复树结构
std::cout << curr->data << " "; // 访问
curr = curr->right;
}
}
}
}
- 序列化与反序列化: 将树结构转换为字符串(如前序遍历带空指针标记)以便存储或传输,并能从字符串重建树。
cpp
// 序列化 (前序遍历)
template <typename T>
std::string serialize(TreeNode<T>* root) {
if (root == nullptr) return "#,";
std::string str = std::to_string(root->data) + ",";
str += serialize(root->left);
str += serialize(root->right);
return str;
}
// 反序列化
template <typename T>
TreeNode<T>* deserialize(std::istringstream& iss) {
std::string token;
if (!std::getline(iss, token, ',')) return nullptr;
if (token == "#") return nullptr;
TreeNode<T>* root = new TreeNode<T>(std::stoi(token));
root->left = deserialize<T>(iss);
root->right = deserialize<T>(iss);
return root;
}
4. 内存管理
- 智能指针: 使用
std::unique_ptr管理节点内存,避免手动delete,减少内存泄漏风险。
cpp
template <typename T>
struct BSTNode {
T data;
std::unique_ptr<BSTNode> left;
std::unique_ptr<BSTNode> right;
BSTNode(T val) : data(val) {}
};
- 析构函数: 若使用原始指针,必须实现递归析构释放所有节点内存。
cpp
template <typename T>
class BST {
private:
TreeNode<T>* root;
void destroyTree(TreeNode<T>* node) {
if (node) {
destroyTree(node->left);
destroyTree(node->right);
delete node;
}
}
public:
~BST() {
destroyTree(root);
}
// ... 其他成员函数
};
5. 性能考量与应用
- 时间复杂度: 平衡树确保插入、删除、查找操作在O(\\log n)时间内完成。
- 空间复杂度: 通常O(n)存储节点,递归遍历栈深度O(\\log n)(平衡树)或O(n)(最坏情况)。
- 应用场景:
- 数据库索引 (B树/B+树是其多路平衡树扩展)
- 文件系统目录结构
- 高效查找、插入、删除操作
- 表达式树 (编译原理)
- 决策树 (机器学习)
总结
掌握二叉树的进阶知识,特别是平衡树原理与实现、高效遍历算法、序列化以及C++特有的内存管理技巧,对于编写高效、健壮的程序至关重要。理解这些概念能帮助你在面对复杂数据结构问题时游刃有余。建议动手实现AVL树或红黑树,并尝试解决LeetCode上的相关题目(如二叉树序列化、最近公共祖先、路径和问题)来巩固理解。