欢迎光临
我们一直在努力

AVL树的介绍和使用

一.AVL树的基本概念

  AVL树是一种特殊的二叉树,AVL树是一颗空树或者具备以下性质:左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。AVL树是通过控制高度差来去调节数据的。

二.平衡因子的介绍

  我们通过平衡因子来确定AVL树是否平衡。

  平衡因子=右子树高度-左子树高度,右边的数据大,而左边的数据小并且存在正负我们的比较是取其绝对值。

  对于一个标准的AVL树来说,平衡因子只能是-1、1、0。平衡因子就像是风向标,来判断AVL树是否平衡,也主要利用平衡因子来判断AVL树的高度关系。

三.AVL树的结构设计

    AVL树的效率比较高,效率严格控制在O(log n)级别。最差情况要远远优于二叉搜索树的做差情况。

  AVL树的节点存储需要左右指针、父结点指针以及平衡因子来完整的组成。

template<class k,class v>
class AVLTree {
    //类型别名:简化节点指针的使用
    typedef AVLTreeNode<k, v> Node;
public:
    //构造基本的框架
    AVLTree():_root(nullptr){}
    //插入、查找等基本操作
    bool Insert(const pair < k, v)& kv);
    Node* Find(const k& key);
    bool IsBalanceTree();
    //用于验证其性质
    void InOrder() {
        _InOrder(_root);
        cout << "完整" << endl;
    }
private:
    Node* _root;
    void _InOrder(Node* root) {
        if (root == nullptr)return;
        _InOrder(root->_left);
        cout << root->_kv.first << " ";
        _InOrder(root->_right);
    }
    int _Height(NOde* root);
    bool _IsBalanceTree(Node* root);
    //重点:旋转操作
    void RotateR(Node* parent);//右单旋
    void RotateL(Node* parent);//左单旋
    void RotateLR(Node* parent);//左右双旋
    void RotateRL(Node* parent);//右左双旋
};

  这就是AVL树的一个基本结构。

四.AVL树的插入

bool AVLTree<K, V>::Insert(const pair<K, V>&amp; kv) {
    // 情况1:树为空,直接创建根节点
    if (_root == nullptr) {
        _root = new Node(kv);
        return true;
    }

    // 情况2:树非空,查找插入位置
    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 {
            // 键值已存在,插入失败(AVL树不允许重复键)
            return false;
        }
    }

    // 找到插入位置,创建新节点并建立父子关系
    cur = new Node(kv);
    if (parent->_kv.first < kv.first) {
        // 新节点为父节点的右孩子
        parent->_right = cur;
    } else {
        // 新节点为父节点的左孩子
        parent->_left = cur;
    }
    // 建立父节点指针(关键:用于后续回溯)
    cur->_parent = parent;

    // 后续步骤:更新平衡因子并调整平衡(见3.3节)
    // …
}
  我们也可以通过图像来更好地理解:

  我们还要注意的是对平衡因子的更新。

// 接3.2节代码,继续在Insert函数中实现
// 步骤2:回溯更新平衡因子
while (parent) {
    // 1. 更新当前父节点的平衡因子
    if (cur == parent->_left) {
        // 新节点是父节点的左孩子,父节点bf–
        parent->_bf–;
    } else {
        // 新节点是父节点的右孩子,父节点bf++
        parent->_bf++;
    }

    // 2. 判断是否需要继续更新或调整平衡
    if (parent->_bf == 0) {
        // 情况1:bf变为0,子树高度不变,停止更新
        break;
    } else if (parent->_bf == 1 || parent->_bf == -1) {
        // 情况2:bf变为±1,子树高度增加1,继续向上更新
        cur = parent;
        parent = parent->_parent;
    } else if (parent->_bf == 2 || parent->_bf == -2) {
        // 情况3:bf变为±2,子树失衡,需要旋转调整
        // 根据失衡类型选择对应的旋转操作
        if (parent->_bf == 2) {
            // 右子树过高,需左单旋或右左双旋
            if (cur->_bf == 1) {
                // 右子树的右子树过高(RR型),左单旋
                RotateL(parent);
            } else {
                // 右子树的左子树过高(RL型),右左双旋
                RotateRL(parent);
            }
        } else { // parent->_bf == -2
            // 左子树过高,需右单旋或左右双旋
            if (cur->_bf == -1) {
                // 左子树的左子树过高(LL型),右单旋
                RotateR(parent);
            } else {
                // 左子树的右子树过高(LR型),左右双旋
                RotateLR(parent);
            }
        }
        // 旋转后子树高度恢复,无需继续向上更新,插入结束
        break;
    } else {
        // 异常情况:bf绝对值超过2(代码逻辑错误)
        assert(false);
    }
}

return true;
五.AVL树的旋转

  AVL树的旋转也是最重要和最有难度的内容。主要分为右单旋、左单旋、左右双旋、右左双旋。

        parent (bf=-2)
       /
    subL (bf=-1)
   /
 newNode
如果出现这样的情况,就无法满足AVL树的条件了,但是可以通过旋转来实现。

void AVLTree<K, V>::RotateR(Node* parent) {
    // 1. 保存关键节点
    Node* subL = parent->_left;    // parent的左孩子(即将成为新根)
    Node* subLR = subL->_right;    // subL的右子树(即将成为parent的左子树)
    Node* parentParent = parent->_parent; // parent的父节点(上层节点)

    // 2. 调整subLR与parent的关系
    parent->_left = subLR;
    if (subLR != nullptr) {
        subLR->_parent = parent;
    }

    // 3. 调整parent与subL的关系
    subL->_right = parent;
    parent->_parent = subL;

    // 4. 调整subL与上层节点的关系
    if (parentParent == nullptr) {
        // 情况1:parent是原根节点,subL成为新根
        _root = subL;
        subL->_parent = nullptr;
    } else {
        // 情况2:parent是上层节点的左/右孩子,subL替换其位置
        if (parentParent->_left == parent) {
            parentParent->_left = subL;
        } else {
            parentParent->_right = subL;
        }
        subL->_parent = parentParent;
    }

    // 5. 重置平衡因子(旋转后subL和parent均平衡)
    parent->_bf = 0;
    subL->_bf = 0;
}

左单旋也是基本相同:

void AVLTree<K, V>::RotateL(Node* parent) {
    // 1. 保存关键节点
    Node* subR = parent->_right;   // parent的右孩子(即将成为新根)
    Node* subRL = subR->_left;     // subR的左子树(即将成为parent的右子树)
    Node* parentParent = parent->_parent; // parent的父节点

    // 2. 调整subRL与parent的关系
    parent->_right = subRL;
    if (subRL != nullptr) {
        subRL->_parent = parent;
    }

    // 3. 调整parent与subR的关系
    subR->_left = parent;
    parent->_parent = subR;

    // 4. 调整subR与上层节点的关系
    if (parentParent == nullptr) {
        // 情况1:parent是原根节点,subR成为新根
        _root = subR;
        subR->_parent = nullptr;
    } else {
        // 情况2:parent是上层节点的左/右孩子,subR替换其位置
        if (parentParent->_left == parent) {
            parentParent->_left = subR;
        } else {
            parentParent->_right = subR;
        }
        subR->_parent = parentParent;
    }

    // 5. 重置平衡因子
    parent->_bf = 0;
    subR->_bf = 0;
}

左右双旋:

void AVLTree<K, V>::RotateLR(Node* parent) {
    // 1. 保存关键节点
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    // 记录subLR的平衡因子(用于后续重置)
    int bf = subLR->_bf;

    // 2. 第一步:以subL为旋转点进行左单旋(转为LL型)
    RotateL(subL);
    // 3. 第二步:以parent为旋转点进行右单旋(恢复平衡)
    RotateR(parent);

    // 4. 根据subLR的原始bf重置平衡因子
    if (bf == 0) {
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 0;
    } else if (bf == -1) {
        // subLR的左子树插入节点,parent的右子树高度增加
        subL->_bf = 0;
        subLR->_bf = 0;
        parent->_bf = 1;
    } else if (bf == 1) {
        // subLR的右子树插入节点,subL的左子树高度增加
        subL->_bf = -1;
        subLR->_bf = 0;
        parent->_bf = 0;
    } else {
        assert(false); // 异常情况
    }
}

右左双旋:

void AVLTree<K, V>::RotateRL(Node* parent) {
    // 1. 保存关键节点
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    // 记录subRL的平衡因子
    int bf = subRL->_bf;

    // 2. 第一步:以subR为旋转点进行右单旋(转为RR型)
    RotateR(subR);
    // 3. 第二步:以parent为旋转点进行左单旋(恢复平衡)
    RotateL(parent);

    // 4. 根据subRL的原始bf重置平衡因子
    if (bf == 0) {
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = 0;
    } else if (bf == 1) {
        // subRL的右子树插入节点,parent的左子树高度增加
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = -1;
    } else if (bf == -1) {
        // subRL的左子树插入节点,subR的右子树高度增加
        subR->_bf = 1;
        subRL->_bf = 0;
        parent->_bf = 0;
    } else {
        assert(false); // 异常情况
    }
}

AVL树的难度主要体现在旋转和对其平衡的控制!

赞(0)
未经允许不得转载:171主机测评 » AVL树的介绍和使用
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址