浅谈平衡搜索树之AVL树

来源:互联网 发布:淘宝公司在哪里 编辑:程序博客网 时间:2024/06/05 05:33

对于上篇的二叉搜索树,我们可以讨论一下它的时间复杂度。在最好的情况下,也就是这颗二叉搜索树是一棵完全二叉树的情况下,它的查找效率是lgN(以2为底N的对数,这里为了方便起见这样写),但是在最坏的情况下,也就是说,在插入的时候是有序插入,这颗二叉搜索树达到了以下的状态
这里写图片描述
这个时候这颗二叉搜索是一棵极度不平衡的树,它的时间复杂度是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,子树的高度没有变。
这里写图片描述
②._bf变为1或-1,说明原来的 _bf是0,子树的高度变了,需要继续向上调整。
这里写图片描述
③._bf变为2或-2,说明原来的 _bf是1或-1,这个时候就需要通过旋转来调整树的高度。
这里写图片描述

下面来仔细介绍一下旋转这种方法。
1.左旋
这里写图片描述
如上图所示,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.右旋
这里写图片描述
右旋与左旋的原理是相似的,这里就不再重复了,只是方向反了一下。发生右旋的条件是parent的_bf是-2,parent的左孩子的 _bf是-1。
3.右左双旋
这里写图片描述
顾名思义,如上图,右左双旋就是先以30为轴进行一个右旋,再以10为轴进行一个左旋。这有三种情况,一种就是在如上图在c树插入了一个节点改变了高度,另一种就是在b树插入了一个节点,还有一种是20就是新增节点,b、c都不存在。可以发现的是,进行右左双旋的条件是,parent的_bf是2,parent的右孩子的 _bf是-1。
4.左右双旋
这里写图片描述
与右左双旋类似,先进行左旋,再进行右单旋。也是有三种情况,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。在代码中我们可以看到,用注释注释了的判平衡的算法效率很低,它的思想有一点像用递归方法求斐波那契数列,会有重复求解的过程,效率非常低,所以这里只写出来并不用。

最终测试的代码如下,我们每插入一个值就判断这棵树平不平衡。
这里写图片描述