C++二叉树进阶:平衡树与高效遍历

  1. 二叉搜索树的高级操作

二叉搜索树的核心在于其有序性:左子树所有节点值 < 根节点值 < 右子树所有节点值。

  • 插入与删除的递归/迭代实现:
    • 递归实现简洁,但深度过大时可能导致栈溢出。
    • 迭代实现更安全,需仔细处理指针的重新链接。
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上的相关题目(如二叉树序列化、最近公共祖先、路径和问题)来巩固理解。

相关推荐
苏宸啊16 小时前
C++(二)类和对象上篇
开发语言·c++
fqbqrr16 小时前
2601C++,编译时连接两个串指针
c++
superman超哥16 小时前
双端迭代器(DoubleEndedIterator):Rust双向遍历的优雅实现
开发语言·后端·rust·双端迭代器·rust双向遍历
嵌入式进阶行者16 小时前
【算法】TLV格式解析实例:华为OD机考双机位A卷 - TLV解析 Ⅱ
数据结构·c++·算法
OC溥哥99916 小时前
Paper MinecraftV3.0重大更新(下界更新)我的世界C++2D版本隆重推出,拷贝即玩!
java·c++·算法
Jayden_Ruan16 小时前
C++蛇形方阵
开发语言·c++·算法
星火开发设计16 小时前
C++ map 全面解析与实战指南
java·数据结构·c++·学习·算法·map·知识
老鱼说AI16 小时前
现代计算机系统1.2:程序的生命周期从 C/C++ 到 Rust
c语言·c++·算法
仰泳的熊猫16 小时前
题目1099:校门外的树
数据结构·c++·算法·蓝桥杯
心.c16 小时前
如何基于 RAG 技术,搭建一个专属的智能 Agent 平台
开发语言·前端·vue.js