C++ AVL树实现详解

1. AVL树的概念与原理

AVL树是最早发明的自平衡二叉查找树,由前苏联科学家G. M. Adelson-Velsky和E. M. Landis在1962年的论文《An algorithm for the organization of information》中提出。AVL树得名于两位发明者的姓氏首字母。

1.1 AVL树的定义

AVL树是一棵空树,或者是具有以下性质的二叉搜索树:

  1. 它的左右子树都是AVL树
  2. 左右子树的高度差的绝对值不超过1

AVL树通过严格控制高度差来维持平衡,是一种高度平衡的二叉搜索树。

1.2 平衡因子

在AVL树的实现中,我们引入平衡因子(Balance Factor)的概念:

  • 每个节点都有一个平衡因子
  • 平衡因子 = 右子树高度 - 左子树高度
  • 合法平衡因子值:-1, 0, 1

平衡因子虽然并非AVL树必须的,但它能直观地反映树的平衡状态,便于我们观察和控制树的平衡性。

1.3 为什么高度差不超过1?

初学者可能会问:为什么不要求高度差为0,这样不是更平衡吗?通过分析可以发现:

  1. 在某些情况下(如节点数为2、4等),无法做到高度差为0
  2. 允许1的高度差可以在平衡性和实现复杂度之间取得良好平衡

1.4 AVL树的性能

AVL树的节点分布与完全二叉树类似,高度始终控制在O(logN),因此其增删查改操作的时间复杂度都能保证在O(logN),相比普通二叉搜索树有本质提升。
在这里插入图片描述
在这里插入图片描述

2. AVL树的实现

2.1 AVL树的结构

template<class K, class V>
struct AVLTreeNode {
    pair<K, V> _kv;
    AVLTreeNode<K, V>* _left;
    AVLTreeNode<K, V>* _right;
    AVLTreeNode<K, V>* _parent;  // 需要parent指针来更新平衡因子
    int _bf;  // balance factor

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

template<class K, class V>
class AVLTree {
    typedef AVLTreeNode<K, V> Node;
public:
    // 接口函数
private:
    Node* _root = nullptr;
};

2.2 AVL树的插入操作

插入操作分为几个关键步骤:

  1. 按二叉搜索树规则插入新节点
  2. 更新平衡因子:从新节点向上更新到根节点路径上的平衡因子
  3. 检查平衡性:如果发现不平衡,进行旋转调整
  4. 旋转调整:通过旋转恢复平衡,同时降低子树高度
2.2.1 平衡因子更新规则
  • 平衡因子 = 右子树高度 - 左子树高度
  • 只有子树高度变化才会影响当前节点的平衡因子
  • 插入节点后平衡因子更新:
    • 新节点在父节点右子树:父节点平衡因子++
    • 新节点在父节点左子树:父节点平衡因子–
2.2.2 更新停止条件
  1. 平衡因子变为0:说明原本一边高一边低的子树现在两边高度相同,子树高度不变,更新结束
  2. 平衡因子变为1或-1:说明原本平衡的子树现在一边高一边低,子树高度增加1,需要继续向上更新
  3. 平衡因子变为2或-2:说明子树已不平衡,需要进行旋转调整,旋转后更新结束
  • 更新到10结点,平衡因子为2,10所在的子树已经不平衡,需要旋转处理
    在这里插入图片描述
  • 更新到中间结点,3为根的子树高度不变,不会影响上一层,更新结束
    在这里插入图片描述
  • 最坏更新到根停止
    在这里插入图片描述
2.2.3 插入实现代码
bool Insert(const pair<K, V>& kv) {
    if (_root == nullptr) {
        _root = new Node(kv);
        return true;
    }

    Node* parent = nullptr;
    Node* cur = _root;
    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;
        }
        else if (parent->_bf == 1 || parent->_bf == -1) {
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2) {
            // 不平衡,需要旋转处理
            // ...旋转代码将在下面介绍
            break;
        }
        else {
            assert(false);
        }
    }
    return true;
}

2.3 旋转操作

当树失去平衡时,需要通过旋转来恢复平衡。旋转操作有四种基本类型:

2.3.1 旋转原则
  1. 保持二叉搜索树的性质不变
  2. 使不平衡的树恢复平衡
  3. 降低旋转子树的高度
2.3.2 右单旋

适用场景:左子树过高(平衡因子为-2),且左子树的左子树较高(左子树的平衡因子为-1)

操作步骤

  1. 将父节点的左子节点提升为新根
  2. 将新根的右子树作为原父节点的左子树
  3. 将原父节点作为新根的右子树
  • 简记:右单旋就是把右边压下去(对照图理解,左单旋同理)
    在这里插入图片描述
void RotateR(Node* parent) {
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

    parent->_left = subLR;
    if (subLR) subLR->_parent = parent;

    Node* parentParent = parent->_parent;
    subL->_right = parent;
    parent->_parent = subL;

    if (parentParent == nullptr) {
        _root = subL;
        subL->_parent = nullptr;
    }
    else {
        if (parent == parentParent->_left) {
            parentParent->_left = subL;
        }
        else {
            parentParent->_right = subL;
        }
        subL->_parent = parentParent;
    }
    parent->_bf = subL->_bf = 0;
}
2.3.3 左单旋

适用场景:右子树过高(平衡因子为2),且右子树的右子树较高(右子树的平衡因子为1)

操作步骤

  1. 将父节点的右子节点提升为新根
  2. 将新根的左子树作为原父节点的右子树
  3. 将原父节点作为新根的左子树
    在这里插入图片描述
void RotateL(Node* parent) {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;

    parent->_right = subRL;
    if (subRL) subRL->_parent = parent;

    Node* parentParent = parent->_parent;
    subR->_left = parent;
    parent->_parent = subR;

    if (parentParent == nullptr) {
        _root = subR;
        subR->_parent = nullptr;
    }
    else {
        if (parent == parentParent->_left) {
            parentParent->_left = subR;
        }
        else {
            parentParent->_right = subR;
        }
        subR->_parent = parentParent;
    }
    parent->_bf = subR->_bf = 0;
}
2.3.4 左右双旋

适用场景:左子树过高(平衡因子为-2),且左子树的右子树较高(左子树的平衡因子为1)

操作步骤

  1. 先对左子节点进行左旋
  2. 再对父节点进行右旋
  3. 三个场景如图
    在这里插入图片描述
void RotateLR(Node* parent) {
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;

    RotateL(parent->_left);
    RotateR(parent);

    // 根据subLR原来的平衡因子调整各节点平衡因子
    if (bf == 0) {
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 0;
    }
    else if (bf == -1) {
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 1;
    }
    else if (bf == 1) {
        subL->_bf = -1;
        subLR->_bf = 0;
        parent->_bf = 0;
    }
    else {
        assert(false);
    }
}
2.3.5 右左双旋

适用场景:右子树过高(平衡因子为2),且右子树的左子树较高(右子树的平衡因子为-1)

操作步骤

  1. 先对右子节点进行右旋
  2. 再对父节点进行左旋
  3. 同上
void RotateRL(Node* parent) {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    int bf = subRL->_bf;

    RotateR(parent->_right);
    RotateL(parent);

    // 根据subRL原来的平衡因子调整各节点平衡因子
    if (bf == 0) {
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = 0;
    }
    else if (bf == 1) {
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = -1;
    }
    else if (bf == -1) {
        subR->_bf = 1;
        subRL->_bf = 0;
        parent->_bf = 0;
    }
    else {
        assert(false);
    }
}

2.4 AVL树的查找

查找操作与普通二叉搜索树相同,时间复杂度为O(logN):

Node* Find(const K& key) {
    Node* cur = _root;
    while (cur) {
        if (cur->_kv.first < key) {
            cur = cur->_right;
        }
        else if (cur->_kv.first > key) {
            cur = cur->_left;
        }
        else {
            return cur;
        }
    }
    return nullptr;
}

2.5 AVL树的平衡检测

为了验证AVL树的实现是否正确,我们可以通过检查每个节点的平衡因子和子树高度差来进行验证:

int _Height(Node* root) {
    if (root == nullptr) return 0;
    int leftHeight = _Height(root->_left);
    int rightHeight = _Height(root->_right);
    return max(leftHeight, rightHeight) + 1;
}

bool _IsBalanceTree(Node* root) {
    if (root == nullptr) return true;
    
    int leftHeight = _Height(root->_left);
    int rightHeight = _Height(root->_right);
    int diff = rightHeight - leftHeight;
    
    if (abs(diff) >= 2) {
        cout << root->_kv.first << "高度差异常" << endl;
        return false;
    }
    if (root->_bf != diff) {
        cout << root->_kv.first << "平衡因子异常" << endl;
        return false;
    }
    
    return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}

3.总代码实现

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace sty {
    // AVL树节点结构体
    template<class K, class V>
    struct AVLTreeNode {
        pair<K, V> _kv;                  // 键值对
        AVLTreeNode<K, V>* _left;         // 左子节点
        AVLTreeNode<K, V>* _right;        // 右子节点
        AVLTreeNode<K, V>* _parent;       // 父节点指针(用于平衡因子更新)
        int _bf;                         // 平衡因子(balance factor)

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

    // AVL树类
    template<class K, class V>
    class AVLTree {
        typedef AVLTreeNode<K, V> Node;
    public:
        // 插入键值对
        bool Insert(const pair<K, V>& kv) {
            // 如果树为空,直接创建根节点
            if (_root == nullptr) {
                _root = new Node(kv);
                return true;
            }

            // 查找插入位置
            Node* parent = nullptr;
            Node* cur = _root;
            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--;  // 插入在左子树,平衡因子减1
                else
                    parent->_bf++;  // 插入在右子树,平衡因子加1

                // 根据更新后的平衡因子决定下一步操作
                if (parent->_bf == 0) {
                    // 平衡因子为0,说明子树高度不变,更新结束
                    break;
                }
                else if (parent->_bf == 1 || parent->_bf == -1) {
                    // 平衡因子为1或-1,说明子树高度变化,需要继续向上更新
                    cur = parent;
                    parent = parent->_parent;
                }
                else if (parent->_bf == 2 || parent->_bf == -2) {
                    // 平衡因子为2或-2,需要进行旋转调整
                    if (parent->_bf == -2 && cur->_bf == -1) {
                        // 左左情况 - 右单旋
                        RotateR(parent);
                    }
                    else if (parent->_bf == 2 && cur->_bf == 1) {
                        // 右右情况 - 左单旋
                        RotateL(parent);
                    }
                    else if (parent->_bf == -2 && cur->_bf == 1) {
                        // 左右情况 - 左右双旋
                        RotateLR(parent);
                    }
                    else if (parent->_bf == 2 && cur->_bf == -1) {
                        // 右左情况 - 右左双旋
                        RotateRL(parent);
                    }
                    else {
                        assert(false);  // 不应该出现的情况
                    }
                    break;  // 旋转后子树高度恢复,更新结束
                }
                else {
                    assert(false);  // 平衡因子异常
                }
            }
            return true;
        }

        // 右单旋(处理左左不平衡情况)
        void RotateR(Node* parent) {
            Node* subL = parent->_left;    // parent的左孩子
            Node* subLR = subL->_right;    // subL的右孩子

            // 将subLR作为parent的左孩子
            parent->_left = subLR;
            if (subLR)
                subLR->_parent = parent;

            Node* pParent = parent->_parent;  // parent的父节点

            // 将parent作为subL的右孩子
            subL->_right = parent;
            parent->_parent = subL;

            // 处理旋转后的根节点连接
            if (parent == _root) {
                _root = subL;
                subL->_parent = nullptr;
            }
            else {
                if (pParent->_left == parent) {
                    pParent->_left = subL;
                }
                else {
                    pParent->_right = subL;
                }
                subL->_parent = pParent;
            }

            // 更新平衡因子
            subL->_bf = 0;
            parent->_bf = 0;
        }

        // 左单旋(处理右右不平衡情况)
        void RotateL(Node* parent) {
            Node* subR = parent->_right;   // parent的右孩子
            Node* subRL = subR->_left;     // subR的左孩子

            // 将subRL作为parent的右孩子
            parent->_right = subRL;
            if (subRL)
                subRL->_parent = parent;

            Node* parentParent = parent->_parent;  // parent的父节点

            // 将parent作为subR的左孩子
            subR->_left = parent;
            parent->_parent = subR;

            // 处理旋转后的根节点连接
            if (parentParent == nullptr) {
                _root = subR;
                subR->_parent = nullptr;
            }
            else {
                if (parent == parentParent->_left) {
                    parentParent->_left = subR;
                }
                else {
                    parentParent->_right = subR;
                }
                subR->_parent = parentParent;
            }

            // 更新平衡因子
            parent->_bf = subR->_bf = 0;
        }

        // 左右双旋(处理左右不平衡情况)
        void RotateLR(Node* parent) {
            Node* subL = parent->_left;
            Node* subLR = subL->_right;
            int bf = subLR->_bf;  // 保存旋转前的平衡因子

            // 先对subL进行左旋,再对parent进行右旋
            RotateL(parent->_left);
            RotateR(parent);

            // 根据subLR原来的平衡因子调整各节点平衡因子
            if (bf == -1) {  // 新节点插入在subLR的左子树
                subLR->_bf = 0;
                subL->_bf = 0;
                parent->_bf = 1;
            }
            else if (bf == 1) {  // 新节点插入在subLR的右子树
                subLR->_bf = 0;
                subL->_bf = -1;
                parent->_bf = 0;
            }
            else if (bf == 0) {  // subLR本身就是新节点
                subLR->_bf = 0;
                subL->_bf = 0;
                parent->_bf = 0;
            }
            else {
                assert(false);  // 不应该出现的情况
            }
        }

        // 右左双旋(处理右左不平衡情况)
        void RotateRL(Node* parent) {
            Node* subR = parent->_right;
            Node* subRL = subR->_left;
            int bf = subRL->_bf;  // 保存旋转前的平衡因子

            // 先对subR进行右旋,再对parent进行左旋
            RotateR(parent->_right);
            RotateL(parent);

            // 根据subRL原来的平衡因子调整各节点平衡因子
            if (bf == 0) {  // subRL本身就是新节点
                subR->_bf = 0;
                subRL->_bf = 0;
                parent->_bf = 0;
            }
            else if (bf == 1) {  // 新节点插入在subRL的右子树
                subR->_bf = 0;
                subRL->_bf = 0;
                parent->_bf = -1;
            }
            else if (bf == -1) {  // 新节点插入在subRL的左子树
                subR->_bf = 1;
                subRL->_bf = 0;
                parent->_bf = 0;
            }
            else {
                assert(false);  // 不应该出现的情况
            }
        }

        // 中序遍历
        void InOrder() {
            _InOrder(_root);
            cout << endl;
        }

        // 获取树的高度
        int Height() {
            return _Height(_root);
        }

        // 获取树的节点数量
        int Size() {
            return _Size(_root);
        }

        // 检查树是否平衡
        bool IsBalanceTree() {
            return _IsBalanceTree(_root);
        }

        // 查找键为key的节点
        Node* Find(const K& key) {
            Node* cur = _root;
            while (cur) {
                if (cur->_kv.first < key) {
                    cur = cur->_right;
                }
                else if (cur->_kv.first > key) {
                    cur = cur->_left;
                }
                else {
                    return cur;
                }
            }
            return nullptr;
        }

    private:
        // 递归中序遍历
        void _InOrder(Node* root) {
            if (root == nullptr) {
                return;
            }
            _InOrder(root->_left);
            cout << root->_kv.first << ":" << root->_kv.second << endl;
            _InOrder(root->_right);
        }

        // 递归计算树的高度
        int _Height(Node* root) {
            if (root == nullptr)
                return 0;
            int leftHeight = _Height(root->_left);
            int rightHeight = _Height(root->_right);
            return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
        }

        // 递归计算树的节点数量
        int _Size(Node* root) {
            if (root == nullptr)
                return 0;
            return _Size(root->_left) + _Size(root->_right) + 1;
        }

        // 递归检查树是否平衡
        bool _IsBalanceTree(Node* root) {
            // 空树是平衡的
            if (nullptr == root)
                return true;
            
            // 计算当前节点的平衡因子(左右子树高度差)
            int leftHeight = _Height(root->_left);
            int rightHeight = _Height(root->_right);
            int diff = rightHeight - leftHeight;

            // 检查平衡因子是否合法
            if (abs(diff) >= 2) {  // 高度差超过1
                cout << root->_kv.first << "高度差异常" << endl;
                return false;
            }

            // 检查存储的平衡因子是否与实际计算的一致
            if (root->_bf != diff) {
                cout << root->_kv.first << "平衡因子异常" << endl;
                return false;
            }

            // 递归检查左右子树
            return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
        }

    private:
        Node* _root = nullptr;  // 根节点
    };
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值