【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作

1、AVL树

在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 “An algorithm for the organization of information” 中发表了它。

如果它有n个节点,那么其高度可保持在O(lg n),平均搜索时间复杂度为O(lg n);

特点:

  • 也是特殊的二叉搜索树
  • 它的左右子树都是AVL树
  • 左子树和右子树高度之差(简称平衡因子)的绝对值不超过(-1,0,1)

二叉搜索树的插入,删除,查找等基本操作的实现

2、平衡化旋转

如果在一棵原本是平衡的二叉搜索树中插入一个新节点,就有可能造成不平衡,这个时候就必须调整树的结构,使之平衡化。

如图所示:

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

调整失去平衡的AVL树可以对树进行旋转,根据不同的场景,大致可分为四种旋转:左单旋(LL),右单旋(RR),先左后右双旋(LR),先右后左双旋(RL)。

左单旋(LL):

插入点位于ptr的右子节点的右子树–右右

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

【调整方法】:

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

  • 首先令ptr成为pSubR的左子树
  • pSubRL的值是小于pSubR的值,大于ptr的值,所以可以令pSubRL作为ptr的右子树
  • 判断ptr上一节点的三种情况
  • 更新平衡因子(分析可得,树的其他节点的平衡因子都没有改变,只有ptr和pSubR的平衡因子变为0)

右单旋(RR):

插入点位于ptr的座子节点的左子树–左左

与左单旋十分相似

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

  • 首先令ptr成为pSubL的右子树
  • pSubLR的值是大于pSubL的值,小于ptr的值,所以可以令pSubLR作为ptr的左子树
  • 判断ptr上一节点的三种情况
  • 更新平衡因子

先左后右双旋(LR)

插入点位于ptr左子节点的右子树–左右

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

  • 插入节点后,对不平衡节点(ptr)的左子树进行左单旋
  • 对根为ptr的树进行右单旋
  • 更新平衡因子

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

更新平衡因子的三种情况分析:

bf == -1时:

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

bf == 1时:

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

bf == 0时:

树的各节点都平衡,各节点的平衡因子都为0,所以不用做任何处理。

先右后左双旋(RL)

插入点位于ptr右子节点的左子树–右左

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

  • 插入节点后,对不平衡节点(ptr)的右子树进行右单旋
  • 对根为ptr的树进行左单旋
  • 更新平衡因子(更新平衡因子与LR大致相同,这里不做详细分析)

《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

3、AVL树的插入

在AVL树中插入结点(key,value)的算法:

  • 如果是空树,插入后即为根节点,插入后直接返回true
  • 如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
  • 插入结点
  • 更新平衡因子,对树进行调整

新节点pcur平衡因子为0,但其双亲结点parent的平衡因子有三种情况:

  • 如果parent的平衡因子为0; 即在parent较矮的子树上插入新节点,parent平衡,其高度没有增加,此时从parent到根路径上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。
  • 如果parent的平衡因子的绝对值为1;插入前parent的平衡因子为0,插入后以parent为根的子树没有失去平衡,但该子树的高度增加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性。
  • 在上述2更新后,如果parent平衡因子的绝对值为2;新节点在较高的子树插入,需要做平衡化处理:
    若parent->_bf == 2,说明右子树高,设parent的右子树为subR
    当subR的平衡因子为1,执行左单旋转
    当subR的平衡因子为-1,执行先右后左双旋转
    若parent->_bf == -2,说明左子树高,设parent的左子树为subL
    当subL的平衡因子为-1,执行右单旋转
    当subL的平衡因子为1,执行先左后右双旋转
    旋转后parent为根的子树高度降低,无需继续向上层回溯

AVLTree源码:

AVLTree.h

#pragma once
#include<iostream>

using namespace std;

template<class K,class V>
struct AVLTreeNode{
public:
    AVLTreeNode()
    {}

    AVLTreeNode(const K& key,const V& value)
        :_key(key)
        , _value(value)
        , _bf(0)
        , _pLeft(NULL)
        , _pRight(NULL)
        , _pParent(NULL)
    {}

public:
    AVLTreeNode<K,V>* _pLeft;
    AVLTreeNode<K,V>* _pRight;
    AVLTreeNode<K,V>* _pParent;
    int _bf;
    K _key;
    V _value;
};

template<class K,class V>
class AVLTree{
    typedef AVLTreeNode<K,V> Node;
    typedef Node* pNode;
public:
    AVLTree()
        :_pRoot(NULL)
    {}

    //插入元素
    bool Insert(const K& key, const V& value){
        //如果是空树,插入后即为根节点,插入后直接返回true
        if (NULL == _pRoot){
            _pRoot = new Node(key, value);
            return true;
        }

        //寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回false
        pNode pCur = _pRoot;
        pNode pParent = NULL;
        while (pCur){
            if (key > pCur->_key){
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else if (key < pCur->_key){
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else
                return false;//在寻找的过程中找到key
        }//pCur == NULL

        //插入节点
        pCur = new Node(key, value);
        if (pParent->_key < pCur->_key)
        {
            pParent->_pRight = pCur;
        }
        else
        {
            pParent->_pLeft = pCur;
        }
        pCur->_pParent = pParent;

        //更新平衡因子,调整树

        while (pParent){//最多更新至根节点

            //更新平衡因子,调整树
            if (pCur == pParent->_pLeft){
                pParent->_bf++;
            }
            if (pCur == pParent->_pRight){
                pParent->_bf--;
            }

            //调整树
            if (0 == pParent->_bf){
                //各结点的平衡因子不变,结束平衡化处理
                break;
            }
            else if (1 == pParent->_bf || -1 == pParent->_bf){
                //以parent为根的子树没有失去平衡,但该子树的高度增加,需从parent向根节点方向回溯,继续查看parent的双亲的平衡性
                pCur = pParent;
                pParent = pParent->_pParent;
            }
            else{
                //如果parent平衡因子的绝对值为2,新节点在较高的子树插入,需要做平衡化处理
                if (-2 == pParent->_bf){
                    if (-1 == pCur->_bf){
                        //pParent->-2 pCur->-1 左单旋
                        RotateL(pParent);
                    }
                    if (1 == pCur->_bf){
                        //pParent->-2 pCur->1 先右后左双旋
                        RotateRL(pParent);
                    }
                }
                if (2 == pParent->_bf){
                    if (1 == pCur->_bf){
                        //pParent->2 pCur->1 右单旋
                        RotateR(pParent);
                    }
                    if (-1 == pCur->_bf){
                        //pParent->2 pCur->-1 先左后右双旋
                        RotateLR(pParent);
                    }
                }
                break;
            }
        }
        return true;
    }

    //中序遍历
    void InOder(){
        cout << "中序遍历:"<< endl;
        _InOder(_pRoot);
        cout << endl;
    }

    //判断AVL树是否平衡
    bool IsBalance(){
        int depth;
        return _IsBalance(_pRoot, depth);
    }

    //求取树的深度
    int Depth(){
        return _Depth(_pRoot);
    }

protected:

    //中序遍历
    void _InOder(pNode pRoot){
        if (NULL == pRoot)
            return;
        _InOder(pRoot->_pLeft);
        cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
        _InOder(pRoot->_pRight);
    }

    //左单旋
    void RotateL(pNode pParent){
        if (NULL == pParent)
            return;

        pNode ptr = pParent;
        pNode pSubR = ptr->_pRight;
        pNode pSubRL = pSubR->_pLeft;


        //标记ptr的父母节点
        pNode pPParent = ptr->_pParent;
        //令ptr成为pSubR的左子树
        pSubR->_pLeft = ptr;
        ptr->_pParent = pSubR;

        //pSubRL的值是小于pSubR的值,大于ptr的值,所以可以令pSubRL作为ptr的右子树
        ptr->_pRight = pSubRL;
        if (pSubRL)
            pSubRL->_pParent = ptr;

        pSubR->_pParent = pPParent;

        //判断ptr上一节点的三种情况
        if (NULL == pPParent){
            //ptr为根节点
            _pRoot = pSubR;
        }
        else{
            if (ptr == pPParent->_pLeft)
                pPParent->_pLeft = pSubR;
            else
                pPParent->_pRight = pSubR;
        }

        //更新平衡因子
        ptr->_bf = pSubR->_bf = 0;

    }

    //右单旋
    void RotateR(pNode pParent){
        if (NULL == pParent)
            return;
        pNode ptr = pParent;
        pNode pSubL = ptr->_pLeft;
        pNode pSubLR = pSubL->_pRight;

        //标记ptr的父母节点
        pNode pPParent = ptr->_pParent;
        //令ptr成为pSubL的左子树
        pSubL->_pRight = ptr;
        ptr->_pParent = pSubL;

        //pSubLR的值是大于pSubL的值,小于ptr的值,所以可以令pSubLR作为ptr的左子树
        ptr->_pLeft = pSubLR;
        if (pSubLR)
            pSubLR->_pParent = ptr;

        pSubL->_pParent = pPParent;

        //判断ptr上一节点的三种情况
        if (NULL == pPParent){
            //ptr为根节点
            _pRoot = pSubL;
        }
        else{
            if (ptr == pPParent->_pLeft)
                pPParent->_pLeft = pSubL;
            else
                pPParent->_pRight = pSubL;
        }

        //更新平衡因子
        ptr->_bf = pSubL->_bf = 0;
    }

    //先左后右双旋(LR)
    void RotateLR(pNode pParent){
        if (NULL == pParent)
            return;
        pNode ptr = pParent;
        pNode pSubL = ptr->_pLeft;
        pNode pSubLR = pSubL->_pRight;
        int bf = pSubLR->_bf;

        //插入节点后,对不平衡节点(ptr)的左子树进行左单旋
        RotateL(ptr->_pLeft);
        //对根为ptr的树进行右单旋
        RotateR(ptr);

        //更新平衡因子
        if (-1 == bf){
            pSubL->_bf = 1;
        }
        else if (1 == bf){
            ptr->_bf = -1;
        }
        else
        {
            ;
        }
    }

    //先右后左双旋(RL)
    void RotateRL(pNode pParent){
        if (NULL == pParent)
            return;
        pNode ptr = pParent;
        pNode pSubR = ptr->_pRight;
        pNode pSubRL = pSubR->_pLeft;
        int bf = pSubRL->_bf;

        //插入节点后,对不平衡节点(ptr)的右子树进行右单旋
        RotateR(ptr->_pRight);
        //对根为ptr的树进行左单旋
        RotateL(ptr);

        //更新平衡因子
        if (-1 == bf){
            ptr->_bf = 1;
        }
        else if (1 == bf){
            pSubR->_bf = -1;
        }
        else
        {
            ;
        }
    }

    //判断AVL树是否平衡
    bool _IsBalance(pNode pRoot, int& depth){
        if (NULL == pRoot){
            depth = 0;
            return true;
        }
        int LeftDepth = 0;
        int RightDepth = 0;

        if (false == _IsBalance(pRoot->_pLeft, LeftDepth))
            return false;
        if (false == _IsBalance(pRoot->_pRight, RightDepth))
            return false;
        if (LeftDepth - RightDepth != pRoot->_bf){
            cout << "<" << pRoot->_key << "," << pRoot->_value << "> " << "bf异常:" << pRoot->_bf << endl;
            return false;
        }
        depth = LeftDepth > RightDepth ? (LeftDepth + 1) : (RightDepth + 1);
        return true;
    }

    //求取树的深度
    int _Depth(pNode pRoot){
        if (NULL == pRoot)
            return 0;
        int LeftDepth = 0;
        int RightDepth = 0;
        LeftDepth = _Depth(pRoot->_pLeft);
        RightDepth = _Depth(pRoot->_pRight);
        return LeftDepth > RightDepth ? (LeftDepth + 1) : (RightDepth + 1);

    }

private:
    pNode _pRoot;
};

test.cpp

#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include"AVLTree.h"
using namespace std;


//测试AVL树
void test3(){
    //普通场景
    int arr1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    AVLTree<int, int> tree1;
    for (int i = 0; i < sizeof(arr1) / sizeof(arr1[0]); ++i){
        tree1.Insert(arr1[i], i);
    }
    //tree1是否为二叉搜索树,二叉搜索树的中序遍历是有序的
    tree1.InOder();
    //tree1是否是平衡的
    if (tree1.IsBalance())
        cout << "普通场景:tree1是平衡的" << endl;
    else
        cout << "普通场景:tree1不平衡" << endl;

    //特殊场景
    int arr2[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
    AVLTree<int, int> tree2;
    for (int i = 0; i < sizeof(arr2) / sizeof(arr2[0]); ++i){
        tree2.Insert(arr2[i], i);
    }
    //tree2是否为二叉搜索树,二叉搜索树的中序遍历是有序的
    tree2.InOder();
    //tree2是否是平衡的
    if (tree2.IsBalance())
        cout << "特殊场景:tree2是平衡的" << endl;
    else
        cout << "特殊场景:tree2不平衡" << endl;

}

int main(){
    test3();
    system("pause");
    return 0;
}

程序运行结果:
《【数据结构】AVL树的平衡化旋转及实现AVL树的插入操作》

    原文作者:AVL树
    原文地址: https://blog.csdn.net/sofia_m/article/details/79816454
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞