浅谈平衡搜索树之AVL树

对于上篇的二叉搜索树,我们可以讨论一下它的时间复杂度。在最好的情况下,也就是这颗二叉搜索树是一棵完全二叉树的情况下,它的查找效率是lgN(以2为底N的对数,这里为了方便起见这样写),但是在最坏的情况下,也就是说,在插入的时候是有序插入,这颗二叉搜索树达到了以下的状态
《浅谈平衡搜索树之AVL树》
这个时候这颗二叉搜索是一棵极度不平衡的树,它的时间复杂度是N。显然如果这颗树存了10亿个数据,那么最坏的情况要查找10亿次,而对于lgN这种算法,只需要30次就够了,两者的查找效率显而易见。所以我们在二叉搜索树的基础上,提出了二叉平衡搜索树的概念。本篇暂时只讲二叉平衡搜索树的一个分支AVL树,在下一篇我们将讲到另一个分支红黑树。

AVL树的特点
AVL树右称为高度平衡的二叉搜索树,它能够保持二叉树的高度平衡,减少搜索长度,那么它是怎么来实现的呢?
1.首先它是一棵二叉搜索树,它具有二叉搜索树所具有的所有性质。
2.左右子树的高度之差的绝对值不超过1
3.树中的每个左子树和右子树都是AVL树
4.每个节点多了一个平衡因子(_bf)的概念,它等于右子树的高度减去左子树的高度,它的绝对值不超过1(即为1,-1,0)。

若有N个数,这颗树的高度可以保持在lgN,对于插入查找删除的操作效率都是lgN。

AVL树的增(insert)
对于AVL树,增是它的重点也是它的难点,是它最难理解的地方。会了增,其实删除也是很好理解的。所以本篇我们只讨论增,以及它在增的时候是如何保持它的高度平衡的。

1.首先和二叉搜索树一样的是,一样也是比较插入的节点与当前节点的key值,找到一个为空的位置然后进行插入。
2.如果父亲的左树插入的,平衡因子_bf–;右树增加的,平衡因子 _bf++。
3.插入了节点,会影响它祖先这一条线上的高度,我们通过parent的_bf在通过调整之后的值,来作出相应的平衡调整。
①._bf变为0,说明原来的 _bf是1或-1,子树的高度没有变。
《浅谈平衡搜索树之AVL树》
②._bf变为1或-1,说明原来的 _bf是0,子树的高度变了,需要继续向上调整。
《浅谈平衡搜索树之AVL树》
③._bf变为2或-2,说明原来的 _bf是1或-1,这个时候就需要通过旋转来调整树的高度。
《浅谈平衡搜索树之AVL树》

下面来仔细介绍一下旋转这种方法。
1.左旋
《浅谈平衡搜索树之AVL树》
如上图所示,10的_bf原来是1,20的 _bf原来是0,在20的右树插入一个节点之后,10的 _bf变为了2,20的 _bf变为了1,这个时候就要以10这个节点为轴进行左旋,将b给10的右,10变成20的左,这样一旋转之后,两者的平衡因子都变成了0。由上图可知,发生左旋的条件是parent的 _bf是2,parent的右孩子的 _bf是1。
2.右旋
《浅谈平衡搜索树之AVL树》
右旋与左旋的原理是相似的,这里就不再重复了,只是方向反了一下。发生右旋的条件是parent的_bf是-2,parent的左孩子的 _bf是-1。
3.右左双旋
《浅谈平衡搜索树之AVL树》
顾名思义,如上图,右左双旋就是先以30为轴进行一个右旋,再以10为轴进行一个左旋。这有三种情况,一种就是在如上图在c树插入了一个节点改变了高度,另一种就是在b树插入了一个节点,还有一种是20就是新增节点,b、c都不存在。可以发现的是,进行右左双旋的条件是,parent的_bf是2,parent的右孩子的 _bf是-1。
4.左右双旋
《浅谈平衡搜索树之AVL树》
与右左双旋类似,先进行左旋,再进行右单旋。也是有三种情况,b插入,c插入,20是新增节点。进行左右双旋的条件是,parent的_bf是-2,parent的左孩子的 _bf是1。

下面我们通过代码来实现一棵AVL树。

#pragma once

#include <iostream>
using namespace std;

#include <assert.h>

template <class K,class V>
struct AVLTreeNode
{
    K _key;
    V _value;
    int _bf;

    AVLTreeNode<K, V>* _left;
    AVLTreeNode<K, V>* _right;
    AVLTreeNode<K, V>* _parent;

    AVLTreeNode(const K& key, const V& value)
        :_left(NULL)
        , _right(NULL)
        , _parent(NULL)
        , _key(key)
        , _value(value)
        , _bf(0)
    {}
};

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

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

        Node* cur = _root;
        Node* parent = NULL;
        while (cur)
        {
            if (cur->_key < key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }

        cur = new Node(key, value);
        //让parent的左或右指向cur,cur的parent指向parent
        if (parent->_key < key)
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_left = cur;
            cur->_parent = parent;
        }

        //更新平衡因子
        while (parent)
        {
            //左增加,_bf--
            if (cur == parent->_left)
            {
                parent->_bf--;
            }
            else
            //右增加,_bf++
            {
                parent->_bf++;
            }

            //根据parent更新后的平衡因子调整
            if (parent->_bf == 0)
            //子树高度没变,不用调整,直接退出
            {
                break;
            }
            else if (parent->_bf == 1 || parent->_bf == -1)
            //子树高度变了,继续向上调整
            {
                cur = parent;
                parent = cur->_parent;
            }
            else if (parent->_bf == 2 || parent->_bf == -2)
            //进行旋转
            {
                if (parent->_bf == 2)
                {
                    if (cur->_bf == 1)
                    {
                        RotateL(parent);//左旋
                    }
                    else
                    {
                        RotateRL(parent);//右左双旋
                    }
                }
                else
                {
                    if (cur->_bf == -1)
                    {
                        RotateR(parent);//右旋
                    }
                    else
                    {
                        RotateLR(parent);//左右双旋
                    }
                }
                //旋转之后不用调整了,直接跳出循环
                break;
            }
            else
            //走到这肯定是不平衡了,断言失败
            {
                assert(false);
            }
        }
        return true;
    }

    bool IsBalance()
    {
        //return _IsBalance(_root);
        int height = 0;
        return _IsBalance(_root,height);
    }

    void Inorder()
    {
        _Inorder(_root);
        cout << endl;
    }
protected:
    bool _IsBalance(Node* root)
        //效率慢
    {
        if (root == NULL)
        {
            return true;
        }
        //右树的高度-左树的高度和bf比较
        int leftHeight = _Height(root->_left);
        int rightHeight = _Height(root->_right);

        return abs(rightHeight - leftHeight) < 2
            && _IsBalance(root->_left)
            && _IsBalance(root->_right);
    }

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

        int leftHeight = 0;
        if (_IsBalance(root->_left, leftHeight) == false)
        //只要有一个节点的平衡因子不对立马返回错误,不用再算后面的
        {
            return false;
        }

        int rightHeight = 0;
        if (_IsBalance(root->_right, rightHeight) == false)
        {
            return false;
        }

        if (root->_bf != rightHeight - leftHeight)
        //只要平衡因子不等于右树高度-左树高度就是错误
        {
            cout << "平衡因子异常" << root->_key << endl;
            return false;
        }

        height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
        //返回左树和右树高的那个的高度+1
        return abs(leftHeight - rightHeight) < 2;
    }

    int _Height(Node* root)
    {
        if (root == NULL)
        {
            return 0;
        }
        int left = _Height(root->_left);
        int right = _Height(root->_right);

        return left > right ? left + 1 : right + 1;
        //返回左树和右树高的那个的高度+1
    }

    void RotateR(Node* parent)//右旋
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* grandparent = parent->_parent;

        parent->_left = subLR;

        if (subLR)
            //如果subLR存在,则将它的父亲指向parent
        {
            subLR->_parent = parent;
        }

        subL->_right = parent;
        parent->_parent = subL;

        if (grandparent == NULL)
            //parent是根节点
        {
            _root = subL;
            subL->_parent = NULL;
        }
        else
        {
            if (parent == grandparent->_left)
            {
                grandparent->_left = subL;
            }
            else
            {
                grandparent->_right = subL;
            }
            subL->_parent = grandparent;
        }
        //更新它们的平衡因子
        subL->_bf = parent->_bf = 0;
    }
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* grandparent = parent->_parent;

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

        subR->_left = parent;
        parent->_parent = subR;

        if (grandparent == NULL)
        {
            _root = subR;
            subR->_parent = NULL;
        }
        else
        {
            if (grandparent->_left == parent)
            {
                grandparent->_left = subR;
            }
            else
            {
                grandparent->_right = subR;
            }

            subR->_parent = grandparent;
        }
        subR->_bf = parent->_bf = 0;
    }

    void RotateRL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;

        int bf = subRL->_bf;
        RotateR(subR);
        RotateL(parent);

        if (bf == -1)
            //在subRL的左边插入了一个节点
        {
            parent->_bf = 0;
            subR->_bf = 1;
            subRL->_bf = 0;
        }
        else if (bf == 1)
            //在subRL的右边插入了一个节点
        {
            parent->_bf = -1;
            subR->_bf = 0;
            subRL->_bf = 0;
        }
        else if (bf == 0)
            //subRL是新增节点
        {
            parent->_bf = 0;
            subR->_bf = 0;
            subRL->_bf = 0;
        }
        else
        {
            assert(false);
        }
    }
    void RotateLR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;

        int bf = subLR->_bf;
        RotateL(subL);
        RotateR(parent);

        if (bf == -1)
            //在subLR的左边插入了一个节点
        {
            parent->_bf = 0;
            subL->_bf = 1;
            subLR->_bf = 0;
        }
        else if (bf == 1)
            //在subLR的右边插入了一个节点
        {
            parent->_bf = -1;
            subL->_bf = 0;
            subLR->_bf = 0;
        }
        else if (bf == 0)
            //subLR是新增节点
        {
            parent->_bf = 0;
            subL->_bf = 0;
            subLR->_bf = 0;
        }
        else
        {
            assert(false);
        }
    }

    bool _Inorder(Node* root)
    {
        if (root == NULL)
        {
            return false;
        }

        _Inorder(root->_left);
        cout << root->_key << " ";
        _Inorder(root->_right);

        return true;
    }
private:
    Node* _root;
};

void TestAVLTree()
{
    int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 }; AVLTree<int, int> t; for (size_t i = 0; i < sizeof(a) / sizeof(int); ++i) { t.Insert(a[i], i); t.Inorder(); cout << "IsBalance()?" << t.IsBalance() << endl; } }

这里我们除了实现了一个Insert,还实现了一个判平衡的函数Isbalance。在代码中我们可以看到,用注释注释了的判平衡的算法效率很低,它的思想有一点像用递归方法求斐波那契数列,会有重复求解的过程,效率非常低,所以这里只写出来并不用。

最终测试的代码如下,我们每插入一个值就判断这棵树平不平衡。
《浅谈平衡搜索树之AVL树》

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