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上的相关题目(如二叉树序列化、最近公共祖先、路径和问题)来巩固理解。

相关推荐
猷咪6 分钟前
C++基础
开发语言·c++
IT·小灰灰7 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧9 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q9 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳010 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾10 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB13 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683613 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计27 分钟前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
蒹葭玉树38 分钟前
【C++上岸】C++常见面试题目--操作系统篇(第二十八期)
linux·c++·面试