C++ AVL树

一、AVL树的核心概念

1.1 为何需要AVL树?

普通二叉搜索树(BST)在理想情况下具有O(log n)的查询效率,但在极端情况下(如插入有序序列)会退化为链表,导致时间复杂度恶化为O(n)。这种现象被称为"单枝树"或"歪脖子树"。AVL树通过引入自平衡机制,确保任意节点的左右子树高度差绝对值不超过1,从而维持树的平衡性。

1.2 AVL树的数学特性

AVL树满足以下两个条件:

  • 左右子树均为AVL树
  • 任意节点的左右子树高度差 ∈ {-1, 0, 1}

这种严格的高度平衡特性使得AVL树的高度h与节点数n满足:

cpp 复制代码
h ≈ 1.44 log₂(n+2) - 0.328

相比BST的最坏情况,AVL树将查找、插入、删除的时间复杂度稳定在O(log n)级别。

二、AVL树的结构设计

2.1 节点定义

cpp 复制代码
template <class K, class V>
struct AVLTreeNode {
    pair<K, V> _kv;            // 键值对存储
    AVLTreeNode* _left;        // 左子节点
    AVLTreeNode* _right;       // 右子节点
    AVLTreeNode* _parent;      // 父节点(三叉链结构)
    int _bf;                   // 平衡因子(Balance Factor)

    AVLTreeNode(const pair<K, V>& kv)
        : _kv(kv), _left(nullptr), _right(nullptr), 
          _parent(nullptr), _bf(0) {}
};

关键设计点

  • 采用三叉链结构(包含parent指针)
  • 平衡因子_bf = 右子树高度 - 左子树高度
  • KV模型支持键值对存储

2.2 核心成员函数

cpp 复制代码
template <class K, class V>
class AVLTree {
public:
    bool Insert(const pair<K, V>& kv);  // 插入操作
    bool Remove(const K& key);          // 删除操作
    Node* Find(const K& key);           // 查找操作
private:
    Node* _root = nullptr;
    // 平衡维护相关辅助函数
    void RotateLeft(Node* parent);      // 左旋
    void RotateRight(Node* parent);     // 右旋
    void UpdateHeight(Node* node);      // 高度更新
};

三、旋转操作详解

3.1 基础旋转

3.1.1 左旋(Left Rotation)

条件:右子树高度 - 左子树高度 ≥ 2

cpp 复制代码
template <typename K, typename V>
void AVLTree<K,V>::RotateLeft(Node* parent) {
    Node* subR = parent->right;
    Node* subRL = subR->left;
    
    parent->right = subRL;
    if (subRL) subRL->parent = parent;

    Node* ppNode = parent->parent;
    subR->left = parent;
    parent->parent = subR;

    // 更新父指针关系
    if (ppNode == nullptr) {
        _root = subR;
        subR->parent = nullptr;
    } else {
        if (ppNode->left == parent) {
            ppNode->left = subR;
        } else {
            ppNode->right = subR;
        }
        subR->parent = ppNode;
    }

    // 更新平衡因子
    UpdateHeight(parent);
    UpdateHeight(subR);
}
3.1.2 右旋(Right Rotation)

触发条件 :左子树高度 - 右子树高度 ≥ 2
操作步骤与左旋对称。

3.2 复合旋转

3.2.1 左-右旋(Left-Right Rotation)

场景 :父节点左倾,左子节点右倾
处理步骤

  1. 对左子节点进行左旋
  2. 对父节点进行右旋
3.2.2 右-左旋(Right-Left Rotation)

场景 :父节点右倾,右子节点左倾
处理步骤

  1. 对右子节点进行右旋
  2. 对父节点进行左旋

四、插入操作实现

4.1 插入流程

cpp 复制代码
bool AVLTree<K,V>::Insert(const pair<K, V>& kv) {
    if (_root == nullptr) {
        _root = new Node(kv);
        return true;
    }

    Node* cur = _root;
    Node* parent = nullptr;
    while (cur) {
        if (cur->_kv.first < kv.first) {
            parent = cur;
            cur = cur->_right;
        } else if (cur->_kv.first > kv.first) {
            parent = cur;
            cur = cur->_left;
        } else {
            return false; // 重复键值
        }
    }

    cur = new Node(kv);
    if (parent->_kv.first < kv.first) {
        parent->_right = cur;
    } else {
        parent->_left = cur;
    }
    cur->_parent = parent;

    // 平衡因子更新与旋转
    while (parent) {
        // 更新平衡因子
        if (cur == parent->_left) {
            parent->_bf--;
        } else {
            parent->_bf++;
        }

        if (parent->_bf == 0) break; // 已平衡
        if (parent->_bf == 1 || parent->_bf == -1) {
            // 继续向上更新
            cur = parent;
            parent = parent->_parent;
        } else {
            // 需要旋转
            if (parent->_bf == 2) {
                if (parent->_right->_bf == 1) {
                    RotateLeft(parent);
                } else {
                    RotateRight(parent->_right);
                    RotateLeft(parent);
                }
            } else { // parent->_bf == -2
                if (parent->_left->_bf == -1) {
                    RotateRight(parent);
                } else {
                    RotateLeft(parent->_left);
                    RotateRight(parent);
                }
            }
            break; // 旋转后子树高度不变,无需继续更新
        }
    }
    return true;
}

AVL树通过平衡策略,在动态数据处理场景中展现出卓越的性能稳定性。尽管其旋转操作实现相对复杂,但其O(log n)的稳定时间复杂度使其成为许多高性能系统的首选数据结构。

相关推荐
祖力552 小时前
数据结构的基本概念与单向链表(链表数据类型构造、创建、头插、尾插、头删、尾删)
数据结构·链表
WWJA王文举4 小时前
I²C通信完整流程详解:START、地址、ACK、数据、Repeated START和STOP一次讲透
c语言·开发语言
zhanghaha13145 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
白狐_7985 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2586 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
ShineWinsu6 小时前
对于C++:C++11中lambda、function、bind的解析
c++·面试·笔试·开发·lambda·bind·function
码匠许师傅6 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
qq_448011166 小时前
C语言中的动态内存分配
c语言·开发语言·php
船厂电气自动化ai大模型7 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
旖旎夜光7 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口