二叉搜索树-AVL树的实现

首先,AVL树是一棵加了额外平衡条件的搜索树。这是因为普通的搜索树如果插入的key接近有序的话,二叉树将会退化成一个单链表,导致查找的时间复杂度为O(N),而AVL树中用一个平衡因子来制约树的左右子树的高度,保证任何节点的左右子树高度之差最多相差1,这样就能保证树深度的平衡状态。

既然AVL树是一课搜索树,它就满足搜索树的性质,这里就不啰嗦了,可以参考二叉搜索树的基本实现。

AVL实现原理

假设有一组元素 arr = { 2,1,3,4,5,6,7,10,9,8};
《二叉搜索树-AVL树的实现》

可以看出来,只要插入的元素接近有序,普通搜索树就退化为单链表,而反观AVL树经过调整,查找的时间复杂度还是保持在O(log2N)。

逐步看构建这颗AVL的过程:
《二叉搜索树-AVL树的实现》
《二叉搜索树-AVL树的实现》

这里就是整个调整过程,说实话,用windows的画图板画这些图,真的累死我了。

旋转实现

在整个实现过程中涉及到四种旋转,只要调整“插入点至根节点”路径上,平衡状态被破坏节点最深的一个,便可使整棵树重新获得平衡。
设最深节点为X。

  1. 插入点位于左子节点的左子树-左左
  2. 插入点位于左子节点的右子树-左右
  3. 插入点位于右子节点的左子树-右左
  4. 插入点位于右子节点的右子树-右右

左左和右右是对称的,都是在外侧插入,直接采用单旋即可解决,右左和左右两种也是对称的,都是在内侧插入,需要进行双旋转才可。

单旋转(Single Rotation)

《二叉搜索树-AVL树的实现》
上面这是一个右单旋的过程,
下面结合后单旋的代码再来看看,一直用图感觉看的云里雾里是吧。

void _RotateRight(Node* pParent)
    {
        Node* pSubL = pParent->_pLeft;//图中的subL
        Node* pSubLR = pSubL->_pRight;//subLR
        Node* pPParent = pParent->_pParent;//先保存祖父节点
        pParent->_pLeft = pSubLR;//不管subLR是否为空,直接链上就好了
        if (pSubLR)//存在的话让subLR的父节点指向parent就好了
        {
            pSubLR->_pParent = pParent;
        }
        pSubL->_pRight = pParent;
        pParent->_pParent = pSubL;

        if (NULL == pPParent)//祖父为空说明parent是根节点
        {
            _pRoot = pSubL;
            pSubL->_pParent = NULL;
        }
        else
        {
            if (pPParent->_pLeft == pParent)
                pPParent->_pLeft = pSubL;
            else
                pPParent->_pRight = pSubL;

            pSubL->_pParent = pPParent;
        }
        pParent->_bf = pSubL->_bf = 0;//旋转后平衡因子为0
        pParent = pSubL;
    }

双旋转(Double Rotation)

首先为什么称之为双旋转,就是因为可以通过两个单旋转来实现。
《二叉搜索树-AVL树的实现》

void _RotateLR(Node* pParent)
    {
        Node* pSubL = pParent->_pLeft;
        Node* pSubLR = pSubL->_pRight;
        int bf = pSubL->_bf;

        _RotateLeft(pParent->_pLeft);
        _RotateRight(pParent);

        if (-1 == bf)
            pParent->_bf = 1;
        else if(1 == bf)
            pSubL->_bf = -1;

    }

不知道你有没有发现在双旋的代码里,按照刚才的分析,不应该是直接调用两个单旋的函数就好了嘛?怎么还进行了判断和平衡因子的调整?
在单旋这种情况下,只要经过了一次旋转。就保证树的平衡,故而在最后直接将平衡因子置为0,那么双旋也是如此吗?
这里就是一个比较容易疏忽的地方了。
看下面这一种情况:
《二叉搜索树-AVL树的实现》
这几个字母表示的子树是可能存在的,比如当b和c存在时,a和d一定存在,否则在b c节点下面继续插入之前10和30的平衡因子就不满足了。
那么这里就有三种可能。
如下:
1、在b的子树插入
《二叉搜索树-AVL树的实现》
2、在c的子树插入(和b的情况相同,只是旋转方向刚好相反)

10->bf = -1;
20->bf = 0;
30->bf = 0

3、20是新插入的节点
只有这种情况,才是最简单的,只调用两个单旋就可以,不用再调整平衡因子

综上,10和30的平衡因子要看20这个节点的情况。
在两次单旋后判断20的情况进行调整就好了,再回去看就可以理解双旋完后的平衡因子的调整了。

判断是否为AVL树

在AVL树中,这个判断是否为AVL树感觉值得一提。
1:先来看第一种方案的代码:

1 、递归求高度,然后比较左右子树的高度差是否小于2

bool _IsbalanceTree(Node* pRoot)
    {
        if (NULL == pRoot)
            return true;

        int LeftHeight = Height(pRoot->_pLeft);
        int RightHeight = Height(pRoot->_pRight);
        //因为可能当前层高度相同,而子树不满足AVL树的性质,所以要递归判断子树
        return _IsbalanceTree(pRoot->_pLeft) && _IsbalanceTree(pRoot->_pRight) && abs(pRoot->_bf) < 2;

    }

size_t Height(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;
        if (NULL == pRoot->_pLeft&&NULL == pRoot->_pRight)
            return 1;
        size_t LeftHeight = Height(pRoot->_pLeft);
        size_t RightHeight = Height(pRoot->_pRight);
        return LeftHeight > RightHeight ? LeftHeight + 1, RightHeight + 1;
    }

这里看到在我们遍历树时花费O(N),递归求高度又是O(N),这样整个程序下来时间复杂度就变成了O(N)2,这样的效率实在让人不敢恭维,那可以怎样优化一下?

2、在每个节点里多保存一个count的变量

这中方法是一种侵入式变成的思想,你让我判断平衡,我就要在每个节点里多加入一个变量,并且如果整棵树非常大的话,浪费的空间不言而喻,并且多出来的count变量只在这里使用,未免大动干戈了。

3、传参时,传一个count进去

第一种方案之所以效率低,主要是因为计算count采用递归的方案,每一次计算一个根节点都要重新计算它的左右子树,类似于斐波那契数列的优化。
可以逆向思维,要求根节点,我先求左右子树,这样一层一层下去,每一层节点只需计算一次就足够,因为传参时传的是引用。
下面这个代码是目前判断是否为AVL树最为高效的一种方法。

bool _IsbalanceTree(Node* pRoot, int& height)
    {
        if (NULL == pRoot)
        {
            height = 0;
            return true;
        }

        int leftHeight = 0;
        int rightHeight = 0;
        if (_IsbalanceTree(pRoot->_pLeft, leftHeight) == 0)
            return false;

        if (_IsbalanceTree(pRoot->_pRight, rightHeight) == 0)
            return false;

        height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
        return (abs(leftHeight - rightHeight) < 2);
    }

最后给出整个代码的的实现:

#pragma once 
#include<iostream>
using namespace std;

template <class K,class V>
struct AVLTreeNode
{
    AVLTreeNode(const K& key, const V& value)
    :_key(key)
    , _value(value)
    , _pLeft(NULL)
    , _pRight(NULL)
    , _pParent(NULL)
    , _bf(0)
    {};
    AVLTreeNode<K,V> *_pLeft;
    AVLTreeNode<K,V> *_pRight;
    AVLTreeNode<K,V> *_pParent;
    K _key;
    V _value;
    int _bf;
};


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

    AVLTree(const AVLTree<K, V>& t)
    {
        _Copy(_pRoot);
    }

    ~AVLTree()
    {
        _Destory(_pRoot);
    }

    bool Insert(const K& key, const V& value)
    {
        if (NULL == _pRoot)
        {
            _pRoot = new Node(key, value);
            return true;
        }
        Node* pCur = _pRoot;
        Node* pParent = NULL;

        while (pCur)
        {
            if (key < pCur->_key)
            {
                pParent = pCur;//更新双亲
                pCur = pCur->_pLeft;
            }
            else if (key>pCur->_key)
            {
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else
            {
                return false;
            }
        }

        //找到了插入的位置,然后创建待插入的结点
        pCur = new Node(key, value);
        if (key < pParent->_key)
        {
            pParent->_pLeft = pCur;
        }

        else if (key>pParent->_key)
        {
            pParent->_pRight = pCur;
        }
        pCur->_pParent = pParent;

        while (pParent)
        {
            if (pParent->_pLeft == pCur)
                pParent->_bf--;
            else if (pParent->_pRight == pCur)
                pParent->_bf++;

            if (0 == pParent->_bf)
                return true;
            else if (1 == pParent->_bf || -1 == pParent->_bf)
            {
                pCur = pParent;
                pParent = pParent->_pParent;
            }
            else// 进行旋转
            {
                if (2 == pParent->_bf)
                {
                    if (1 == pCur->_bf)
                        _RotateLeft(pParent);
                    else
                        _RotateRL(pParent);
                }
                else
                {
                    if (-1 == pCur->_bf)
                        _RotateRight(pParent);
                    else
                        _RotateLR(pParent);
                }
            }
        }
    }


    void _RotateLeft(Node* pParent)
    {
        Node *pSubR = pParent->_pRight;
        Node *pSubRL = pSubR->_pLeft;
        Node *pPParent = pParent->_pParent;

        pParent->_pRight = pSubRL;
        if (pSubRL)
        {
            pSubRL->_pParent = pParent;
        }
        pSubR->_pLeft = pParent;
        pParent->_pParent = pSubR;

        if (NULL == pPParent)
        {
            _pRoot = pSubR;
            pSubR->_pParent = NULL;
        }
        else
        {
            if (pPParent->_pLeft == pParent)
                pPParent->_pLeft = pSubR;
            else
                pPParent->_pRight = pSubR;

            pSubR->_pParent = pPParent;
        }
        pParent->_bf = pSubR->_bf = 0;
        pParent = pSubR;
    };

    bool IsBalanceTree()
    {
        int height = 0;
        return _IsbalanceTree(_pRoot, height);
    }

    void MidOrder()
    {
        cout << "MidOrder:" << " ";
        _MidOrder(_pRoot);
        cout << endl;
    }

    size_t Height(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;
        if (NULL == pRoot->_pLeft&&NULL == pRoot->_pRight)
            return 1;
        size_t LeftHeight = Height(pRoot->_pLeft);
        size_t RightHeight = Height(pRoot->_pRight);
        return LeftHeight > RightHeight ? LeftHeight + 1, RightHeight + 1;
    }

    Node* Find(const K& key)
    {
        if (_pRoot == NULL)
            return NULL;

        Node* cur = _pRoot;
        if (key < cur->_key)
            _pRoot = cur->_pLeft;
        else if (key > cur->_pRight)
            _pRoot = cur->_pRight;
        else
            return cur;
    }
protected:

    void _RotateRight(Node* pParent)
    {
        Node* pSubL = pParent->_pLeft;
        Node* pSubLR = pSubL->_pRight;
        Node* pPParent = pParent->_pParent;
        pParent->_pLeft = pSubLR;
        if (pSubLR)
        {
            pSubLR->_pParent = pParent;
        }
        pSubL->_pRight = pParent;
        pParent->_pParent = pSubL;

        if (NULL == pPParent)
        {
            _pRoot = pSubL;
            pSubL->_pParent = NULL;
        }
        else
        {
            if (pPParent->_pLeft == pParent)
                pPParent->_pLeft = pSubL;
            else
                pPParent->_pRight = pSubL;

            pSubL->_pParent = pPParent;
        }
        pParent->_bf = pSubL->_bf = 0;
        pParent = pSubL;
    }


    void _RotateLR(Node* pParent)
    {
        Node* pSubL = pParent->_pLeft;
        Node* pSubLR = pSubL->_pRight;
        int bf = pSubL->_bf;

        _RotateLeft(pParent->_pLeft);
        _RotateRight(pParent);

        if (-1 == bf)
            pParent->_bf = 1;
        else if(1 == bf)
            pSubL->_bf = -1;

    }

    void _RotateRL(Node* pParent)
    {
        Node* pSubR = pParent->_pRight;
        Node* pSubRL = pSubR->_pLeft;
        int bf = pSubRL->_bf;

        _RotateRight( pParent->_pRight);
        _RotateLeft( pParent);

        if (1 == bf)
            pParent->_bf = -1;
        else if(-1 == bf)
            pSubR->_bf = 1;

    }

    bool _IsbalanceTree(Node* pRoot)
    {
        if (NULL == pRoot)
            return true;

        int LeftHeight = Height(pRoot->_pLeft);
        int RightHeight = Height(pRoot->_pRight);
        //因为可能当前层高度相同,而子树不满足AVL树的性质,所以要递归判断子树
        return _IsbalanceTree(pRoot->_pLeft) && _IsbalanceTree(pRoot->_pRight) && abs(pRoot->_bf) < 2;

    }

    bool _IsbalanceTree(Node* pRoot, int& height)
    {
        if (NULL == pRoot)
        {
            height = 0;
            return true;
        }

        int leftHeight = 0;
        int rightHeight = 0;
        if (_IsbalanceTree(pRoot->_pLeft, leftHeight) == 0)
            return false;

        if (_IsbalanceTree(pRoot->_pRight, rightHeight) == 0)
            return false;

        height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
        return (abs(leftHeight - rightHeight) < 2);
    }

    void _MidOrder(Node* pRoot)
        {
            if (NULL == pRoot)
                return;
            _MidOrder(pRoot->_pLeft);
            cout << pRoot->_key << " ";
            _MidOrder(pRoot->_pRight);
        }

    void _Copy(Node* root)
    {
        Node* newroot = NULL;

        if (root)
        {
            newroot = root;
            newroot->_pLeft = _Copy(root->_pLeft);
            newroot->_pRight = Copy(root->_pRight);
        }
    }

    void _Destory(Node* root)
    {
        if (root == NULL)
            return;

        _Destory(root->_pLeft);
        _Destory(root->_pRight);
        delete root;
    }
protected:
    Node* _pRoot;
};
    原文作者:AVL树
    原文地址: https://blog.csdn.net/qq_36528114/article/details/78283308
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞