平衡二叉搜索树AVL

一、问题

也许是因为输入的值不够随机,也许经过某些插入或者删除操作,二叉搜索树失去了平衡,造成搜寻效率低下。

比如以下这棵树:

这棵树,说是树,其实它已经退化成链表了,但从概念上来看,它仍是一棵二叉搜索树,只要我们按照逐次增大,如1、2、3、4、5、6的顺序构造一棵二叉搜索树,则形如上图。那么插入的时间复杂度就变成了O(n),导致这种糟糕的情况原因是因为这棵树极其不平衡,右树的重量远大于左树。

因此我们提出了叫平衡二叉搜索树的结构,又称之为AVL树。因为平衡二叉搜索树的发明者为Adel’son-Vel’skii 和Landis二人。
平衡二叉搜索树,它能保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均查找长度

二、原理

所谓树形平衡与否,并没有一个绝对的测量标准。“平衡”的大致意义是:没有任何一个节点过深(深度过大).不同的平衡条件,造就出不同的效率表现,以及不同的实现复杂度。有数种特殊结构如AVL-Tree,RB-Tree,AA-Tree, 均可实现平衡二叉搜索树,它们都比一般的(无法绝对维持平衡的)二叉搜索树复杂,因此,插人节点和删除节点的平均时间也比较长,但是它们可以避免极难应付的最坏(高度不平衡)情况,而且由于它们总是保持某种程度的平衡,所以元素的访问(搜寻)时间平均而言,也就比较少.其搜寻时间可节省25%左右。

AVL树的性质:

  • 左子树与右子树高度之差的绝对值不超过1
  • 树的每个左子树和右子树都是AVL树
  • 每一个节点都有一个平衡因子(balance factor),任一节点的平衡因子是-1、0、1(每一个节点的平衡因子 = 右子树高度 - 左子树高度)

做到了这点,这棵树看起来就比较平衡了

举个例子,下图左侧所示的是一 个AVL- tree,插 入 了 节 点 11之 后 ( 图 右 ) , 灰 色 节 点  违 反 平 衡 条 件 • 由 于 只 有 “ 插 入 点 至 根 节 点 ” 路 径 上 的 各 许 点 可 能  改 变 平 衡 状 态 , 因 此,只 要 调 整 其 中 最 深 的 那 个 节 点 , 便 可 使 整 棵 树 重 新 获 得 平 衡 .

 三、调整平衡方案

前面说过,只要调整“插人点至根节点’’路径上,平衡状态被破坏之各节点中最深的那一个,便可使笹棵树重新获得平衡.假设该最深节点为X,由于节点最多拥有两个子节点,而所谓“平衡被破坏”意味着X的左右两棵子树的高度相差2,因此我们可以轻易将情况分为四种
1. 插入点位于X的左子节点的左子树---左左.
2. 插入点位于X的左子节点的右子树----左右
3. 插入点位于X的右子节点的左子树——右左.
4. 插入点位十X的右子节点的右子树-----右右
情况1,4彼此对称,称为外侧(outside)插入,可以采用单旋转操作( single rotation)调整解决。情况2, 3彼此对称,称为内侧(inside)插人,可以采用双旋转操作( double rotation)调整解决.

在平衡搜索树中进行插入结点时,有可能会破坏整棵树的平衡。为了保证平衡不被破坏,就要对一些节点进行旋转,从而来降低树的高度,这样也能保证树的平衡。

1、单旋转

 

(上图中的▲结点有可能是NULL,也有可能不为空。。。下同)

从图中可以看出,进行左单旋时,只是改变了parent的右指针以及subR的左指针指向。将subR的左子树(subRL)作为parent的右子树,并让parent作为subR的左子树。很明显,这样做就降低了这棵树的高度。

进行旋转时需要注意的两点:

1.改变subRL->_parent指向时,需要判断subRL是否为NULL,如果为空,就不能对其解引用。

2.parent是否为根节点?如果parent为根节点,那么旋转完成后只需将subR赋给根节点即可;但如果parent不为根节点,即parent是某一节点ppNode的子树,就要判断parent在ppNode的左还是右,这样才能确定subR的位置。

void RotateLeft(Node* parent)	//左单旋
	{		
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
 
		parent->_right = subRL;	//先改变parent的右指针
		if (subRL)	//subRL可能为NULL
		{
			subRL->_parent = parent;
		}
 
		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
 
		if (ppNode == NULL)
		{
			_root = subR;
			subR->_parent = NULL;
		}
		else
		{
			//判断subR应链接在ppNode的左子树还是右子树
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;
 
			subR->_parent = ppNode;
		}
	}

左左外侧插入与右右外侧方式差不多,这里不详细介绍

2、左右双旋

了解了单旋之后,双旋就比较简单,只是进行了两步单旋而已

void RotateLR(Node* parent)		//左右双旋
	{
		RotateLeft(parent->_left);
		RotateRight(parent);
 
	}

 

 

四、代码

AVL树实现代码整体如下:

#pragma once

//AVLTree树节点定义
template <class K,class V>
struct AVLTreeNode
{
    K _key;
    V _val;

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

    int _bf;    //平衡因子

    AVLTreeNode(const K& key, const V& val)
        :_key(key)
        , _val(val)
        , _left(NULL)
        , _right(NULL)
        , _parent(NULL)
        , _bf(0)
    {}

};

//AVLTree类的定义
template <class K,class V>
class AVLTree
{
    typedef AVLTreeNode<K, V> Node;
public:
    AVLTree()   //构造函数
        :_root(NULL)
    {}

    bool AVLInsert(K key,V val);    //插入节点

    void RotateL(Node* parent);     //  左单旋
    void RotateR(Node* parent);     //  右单旋
    void RotateLR(Node* parent);    //  左右双旋
    void RotateRL(Node* parent);    //  右左双旋

    bool _IsBalance(Node* root, int& height)    //判断是否平衡
    {
        if (root == NULL)
        {
            height = 0;
            return true;
        }

        int leftHeight = 0;
        int rightHeight = 0;
        if (_IsBalance(root->_left, leftHeight)
            && _IsBalance(root->_right, rightHeight))
        {
            if (rightHeight - leftHeight != root->_bf)
            {
                cout << "平衡因子异常" << root->_key << endl;
                return false;
            }

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

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

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

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

    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }

private:
    Node* _root;
};

template <class K,class V>
bool AVLTree<K, V>::AVLInsert(K key, V val)
{
    //1.根节点为空,直接插入
    if (_root == NULL)
    {
        _root = new Node(key, val);
        return true;
    }
    //2.根节点不为空
    else
    {
        Node* cur = _root;
        Node* parent =NULL;
        //a)找到要插入节点的位置
        while (cur)
        {
            parent = cur;
            if (cur->_key > key)
                cur = cur->_left;
            else if (cur->_key < key)
                cur = cur->_right;
            else
                return false;   //不允许出现重复元素的节点
        }
        //b)插入新节点
        cur = new Node(key, val);
        if (parent->_key>key)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }

        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        //c)插入完成后,调整平衡因子
        while (parent)
        {
            if (cur == parent->_left)//插入节点在左子树父节点bf--,反之++
            parent->_bf--;
        else
            parent->_bf++;

            //1)插入新节点后,parent->bf==0;说明高度没变,平衡,返回
            if (parent->_bf == 0)
                break;
            //2)插入节点后parent->_bf==-1||parent->_bf==1;说明子树高度改变,则继续向上调整
            else if (parent->_bf == -1 || parent->_bf == 1)
            {
                cur = parent;
                parent = parent->_parent;
            }
            //3)插入节点后parent->_bf==-2||parent->_bf==2;说明已经不平衡,需要旋转
            else
            {
                if (parent->_bf == 2)
                {
                    if (cur->_bf == 1)
                        RotateL(parent);
                    else// (cur->_bf == -1)
                        RotateRL(parent);
                }
                else//parent->_bf == -2
                {
                    if (cur->_bf == -1)
                        RotateR(parent);
                    else// (cur->_bf == 1)
                        RotateLR(parent);
                }
                break;
            }

        }//end while (parent)
        return true;
    }
}

template <class K, class V>
void AVLTree<K, V>::RotateL(Node* parent)
{
    Node*subR = parent->_right;
    Node*subRL = subR->_left;
    Node*pParent = parent->_parent;

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

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

    if (parent == _root)
    {
        _root = subR;
        _root->_parent = NULL;
    }

    else
    {
        if (pParent->_left = parent)
            pParent->_left = subR;
        else
            pParent->_right = subR;

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

template <class K, class V>
void AVLTree<K, V>::RotateR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    Node* ppNode = parent->_parent;

    parent->_left = subLR;
    if (subLR)
        subLR->_parent = parent;

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

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

        subL->_parent = ppNode;
    }

    subL->_bf = parent->_bf = 0;
}

template <class K, class V>
void AVLTree<K, V>::RotateLR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;

    RotateL(parent->_left);
    RotateR(parent);

    if (bf == 0)
    {
        subLR->_bf = subL->_bf = parent->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = 0;
        subL->_bf = -1;
        subLR->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        subL->_bf = 0;
        subLR->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

template <class K, class V>
void AVLTree<K, V>::RotateRL(Node* parent)
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    int bf = subRL->_bf;

    RotateR(parent->_right);
    RotateL(parent);

    if (bf == 0)
    {
        subRL->_bf = subR->_bf = parent->_bf = 0;
    }
    else if (bf == 1)
    {
        subR->_bf = 0;
        parent->_bf = -1;
        subRL->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 0;
        subR->_bf = 1;
        subRL->_bf = 0;
    }
    else
    {
        assert(false);
    }
}


void TestAVLtree() //测试代码
{
    int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
    //{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.AVLInsert(a[i], i);
        cout << a[i] << ":" << t.IsBalance() << endl;
    }

    t.InOrder();
    cout << t.IsBalance() << endl;
}

参考:

https://blog.csdn.net/tanrui519521/article/details/80935348

https://blog.csdn.net/qq_33951180/article/details/52957164

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值