搜索结构之AVL树

AVL树

概念
搜索二叉树虽然降低了查找的效率,但是如果数据有序或者接近有序,那么搜索二叉树就会退化成单枝树,搜索的时间复杂度就会变成O(n),极大的降低了效率,因此我们引入了AVL树,在向树中插入元素的时候,能保证每一个节点的左右子树的高度不会相差1,从而降低了树的高度,提高了查找的效率。
2.我们把具有一下性质的树称之为AVL树:

1. 首先它是二叉搜索树。
2. 二叉树每个节点的左子树和右子树都是AVL树。
3. 每一个节点的平衡因子绝对值不超过1(平衡因子:当前节点右子树高度减去左子树高度)
这里写图片描述
这样就可以降低树的高度,使得AVL树的查找时间复杂度在O(log(n));


AVL树的插入操作
插入操作很有可能导致AVL树不满足性质,所以我们需要旋转操作来使得树满足性质。
具体插入过程:

  1. 当树是空树的时候,直接创建节点,此节点为根节点,满足性质
  2. 当树非空时,先查找当前节点是否在树中存在,若存在,插入失败,直接返回,如果不存在,找到插入位置,插入节点,更新平衡因子,调整树。
    新节点pcur的平衡因子为0,它的双亲的平衡因子有三种情况:
    1.双亲parent的平衡因子为0
    这种情况就是在parent的较矮的子树上插入,那么parent的平衡因子为0,parent平衡,从parent到根节点的路径上的节点的平衡因子都没有发生改变,因此树满足AVL树性质,不需调整。
    2.双亲的parent的平衡因子绝对值为1
    parent节点平衡因子在插入之前为0,插入之后以parent为根的树平衡没有被破坏,但是子树的高度增加了,向上回溯查看节点平衡因子。
    3.在上述2更新完成后,如果parent平衡因子的绝对值为2,新节点在较高子树上进行插入,那么就必须要进行平衡处理:
    如果parent->_buf是2,说明右子树高,设parent的右子树为subR,那么
    当subR->_buf为1时候,执行左单旋转:
    当subR->buf为-1的时候,执行先右后左双旋;
    如果pparent->_buf是-2,说明左子树高,折parent的左子树是subL,那么
    当subL->buf是-1的时候,执行右单旋;
    当subR->buf是1的时候,执行先左后右双旋;
    旋转之后parent为跟的子树高度降低,不需要向上进行回溯。

左单旋
插入节点位于较高右子树的右侧
这里写图片描述

void RotateL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = pParent->_pRight->_pLeft;
        pParent->_pRight = subRL;
        if (subRL)
            subRL->_pParent = pParent;
        subR->_pLeft = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subR;
        subR->_pParent = pparent;
        if (pParent == _pRoot)
        {
            _pRoot = subR;
        }
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subR;
            else
                pparent->_pRight = subR;
        }
        pParent->_buf = subR->_buf = 0;
    }

右单旋
插入节点位于较高左子树的左侧
这里写图片描述

void RotateR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = pParent->_pLeft->_pRight;
        pParent->_pLeft = subLR;
        if (subLR)
            subLR->_pParent = pParent;
        subL->_pRight = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subL;
        subL->_pParent = pparent;
        if (pParent == _pRoot)
            _pRoot = subL;
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subL;
            else
                pparent->_pRight = subL;
        }
        pParent->_buf = subL->_buf = 0;
    }

右左双旋
插入节点位于较高右子树的左侧
这里写图片描述

    void RotateRL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = NULL;
        if (subR != NULL)
            subRL = subR->_pLeft;
        RotateR(subR);
        RotateL(pParent);
        if (subRL->_buf == 1)
            pParent->_buf = -1;
        else if (subRL->_buf == -1)
            pParent->_buf = 0;
    }

左右双旋
插入节点位于较高左子树的右侧
这里写图片描述

void RotateLR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = NULL;
        if (subL != NULL)
            subLR = subL->_pRight;
        RotateL(subL);
        RotateR(pParent);
        if (subLR->_buf == 1)
            pParent->_buf = 0;
        else if (subLR->_buf == -1)
            pParent->_buf = 1;
    }

AVL树的删除

  • 从AVL树中删除一个节点,首先要判断这个节点是否在树中,若存在,删除就可能导致AVL树不满足性质的情况,就要进行旋转调整。
  • 被删除节点存在一下几种情况:
    1.删除节点只有左孩子。
    2.删除节点只有右孩子。
    3.删除节点左右孩子都存在。—->变为删除中序遍历下的第一个节点。
  • 更新parent的平衡因子,如果删除节点是parent 的左孩子,那么平衡因子+1,相反,如果是右孩子,平衡因子-1,根据修改后的parent的平衡因子修改到根节点的所有节点。
    1.如果parent的平衡因子为1或者-1,那么parent的高度不变,以parent为子树的所有节点平衡因子都没有发生变化,不需要调整。
    2.如果parent的平衡因子变成0,虽然以parent为根的树平衡了,但是其高度减1了,这时候就要去检查parent的双亲的平衡因子的情况。
    3.parent的平衡因子为2或者-2,则较矮的子树被缩短,需要进行平衡话旋转。
    令parent较高子树的根为q,根据q的平衡因子,分一下三种情况 :
    如果q的平衡因子为0,执行单旋转恢复parent
    如果q的平衡因子与parent平衡因子(正负)号相同,则执行一个单旋转恢复 parent
    如果q的平衡因子与parent平衡因子(正负)号相反,则执行一个双旋转恢复 parent

AVL树的代码:

#pragma once
#include<iostream>
#include<math.h>
using namespace std;
template<class k, class v>
struct AVLTreeNode
{
    AVLTreeNode(const k& key, const v& value)
        :_key(key)
        ,_value(value)
        ,_buf(0)
        ,_pLeft(NULL)
        ,_pRight(NULL)
        ,_pParent(NULL)
    {}

    AVLTreeNode<k, v>* _pLeft;
    AVLTreeNode<k, v>* _pRight;
    AVLTreeNode<k, v>* _pParent;
    k _key;
    v _value;
    int _buf;
};
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)
    {
        if (_pRoot == NULL)
        {
            _pRoot = new Node(key, value);
            return true;
        }
        pNode pCur = _pRoot;
        pNode pParent = NULL;
        while (pCur)
        {
            if (pCur->_key > key)
            {
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else if (pCur->_key < key)
            {
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else
            {
                cout << "元素已存在,插入失败" << endl;
                return false;
            }
        }
            pCur = new Node(key, value);
            if (pParent->_key > key)
            {
                pParent->_pLeft =pCur;
                pParent->_buf--;
            }
            else
            {
                pParent->_pRight = pCur;
                pParent->_buf++;
            }
            pCur->_pParent = pParent;
            //插入成功,接下来就是进行调整。
            //情况一,如果pParent的平衡因子是0
            while (pParent)
            {
                if (pParent->_pRight == pCur)
                    pParent->_buf++;
                else
                    pParent->_buf--;
                if (pParent->_buf == 0)
                    break;
                else if (pParent->_buf == 1 || pParent->_buf == -1)
                {
                    pCur = pParent;
                    pParent = pCur->_pParent;
                }
                else
                {
                    if (pParent->_buf == 2)
                    {
                        if (pCur->_buf == 1)
                            RotateL(pParent);
                        else if (pCur->_buf == -1)
                            RotateRL(pParent);
                    }
                    else if (pParent->_buf == -2)
                    {
                        if (pCur->_buf == -1)
                            RotateR(pParent);
                        else if (pCur->_buf == 1)
                            RotateLR(pParent);
                    }
                    break;
                }

            }

            return true;
    }
    void InOrder()
    {
        _InOrder(_pRoot);
    }

    bool IsBalance()
    {
        return _IsBalance(_pRoot);
    }

private:
    bool _IsBalance(pNode pRoot)
    {
        if (pRoot == NULL)
            return true;
        int lefttree = _IsBalance(pRoot->_pLeft);
        int righttree = _IsBalance(pRoot->_pRight);
        if (abs(righttree - lefttree) > 1)
            return false;
        return _IsBalance(pRoot->_pLeft) && _IsBalance(pRoot->_pRight);
    }
    int _Height(pNode pRoot)
    {
        if (pRoot == NULL)
            return 0;
        int left = _Height(pRoot->_pLeft);
        int right = _Height(pRoot->_pRight);
        return left > right ? left + 1 : right + 1;
    }
    void RotateL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = pParent->_pRight->_pLeft;
        pParent->_pRight = subRL;
        if (subRL)
            subRL->_pParent = pParent;
        subR->_pLeft = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subR;
        subR->_pParent = pparent;
        if (pParent == _pRoot)
        {
            _pRoot = subR;
        }
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subR;
            else
                pparent->_pRight = subR;
        }
        pParent->_buf = subR->_buf = 0;
    }
    void RotateR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = pParent->_pLeft->_pRight;
        pParent->_pLeft = subLR;
        if (subLR)
            subLR->_pParent = pParent;
        subL->_pRight = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subL;
        subL->_pParent = pparent;
        if (pParent == _pRoot)
            _pRoot = subL;
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subL;
            else
                pparent->_pRight = subL;
        }
        pParent->_buf = subL->_buf = 0;
    }
    void RotateRL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = NULL;
        if (subR != NULL)
            subRL = subR->_pLeft;
        RotateR(subR);
        RotateL(pParent);
        if (subRL->_buf == 1)
            pParent->_buf = -1;
        else if (subRL->_buf == -1)
            pParent->_buf = 0;
    }
    void RotateLR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = NULL;
        if (subL != NULL)
            subLR = subL->_pRight;
        RotateL(subL);
        RotateR(pParent);
        if (subLR->_buf == 1)
            pParent->_buf = 0;
        else if (subLR->_buf == -1)
            pParent->_buf = 1;
    }
    void _InOrder(pNode pRoot)
    {
        if (pRoot)
        {
            _InOrder(pRoot->_pLeft);
            cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
            _InOrder(pRoot->_pRight);
        }

    }

private:
    pNode _pRoot;
};
void TestAVLTreee()
{
    int a[] = { 16,3,7,11,9,26,18,14,15 };
    AVLTree<int, int> avl;
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        avl.Insert(a[i], i);

    }
    avl.InOrder();
}

这里写图片描述

发布了46 篇原创文章 · 获赞 16 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章