AVL树学习笔记

前言

二叉搜索树是map/set/multiset/multimap的底层实现逻辑。二叉搜索树如果在插入的元素有序的话,就会成为一个单向链表。所以增加了平衡因子来调整结构。

特点:1、AVL树的左右子树的高度之差不超过绝对值1。

           2、一个二叉搜索树如果高度平衡,它就是AVL树,假设ta有n个结点,其高度O(log2n),搜索时间复杂度O(log2n)。

但是,AVL树不一定有平衡因子,使用平衡因子只是它的一种实现方式。(对于这点目前有点疑惑,如果没有平衡因子,它又怎么判断是否该调整呢?)

关于AVL树的代码笔记

二叉搜索树

template<class K,class V>
struct AVL_TreeNode
{
    AVL_TreeNode<K,V>* _left;
    AVL_TreeNode<K,V>* _right;
    AVL_TreeNode<K,V>* _parent;
    pair<K,V> _KV;              //键值对
    int _bf;                    //balance factor的缩写
    AVL_TreeNode(const pair<K,V>& kv)
        :_left(nullptr),
         _right(nullptr),
         _parent(nullptr),
         _kv(kv),
         _bf(0)
    {};
};
template<class K,class V>
class AVL_Tree
{
    typedef AVL_TreeNode<K,V> Node;
public:
    bool Insert(const pair<K,V>& kv)
    {
        if(root==nullptr)//一开始第一次情况
        {
            _root=new Node(kv);
            return true;
        }
        
        Node* parent =nullptr;
        Node* cur=_root;
        while(cur)
        {
            if(cur->_kv.first>kv.first)//插入的比目前大往右走
            {
                parent=cur;
                cur=cur->_right;
            }
            else if(cur->_kv.first<kv.first)//插入的比目前小往左走
            {
                parent =cur;
                cur=cur->_left;
            }
            else
            {
                return false;//相等及存在,去重
            }
        }
        cur=new Node(kv);
        if(parent->_kv.first>kv.first)
        {
            parent->_left=cur;
        }
        else 
        {
            parent->_right=cur;
        }
        cur->_parent=parent;
        //搜索树部分结束
        //接下来是平衡因子的更新,根据parent的_bf的变化进行更新
        //向左放置-1,向右放置+1,本质是左右子树高度差
        while(parent)
        {
            if(cur==parent->_right)
            {
                parent->_bf++;
            }
            else
            {
                parent->_bf--;
            }
            if(parent->_bf==1||parent->_bf==-1)//向前更新
            {
                parent=parent->_parent;
                cur=cur->_parent;
            }
            else if(parent->_bf==0)
            {
                break;//没问题不更新
            }
            else if(parent->_bf==2||parent->_bf==-2)//树的两边不平衡了,需要处理
            {
                //处理的情况单独写。
            }
        }
        return true;
    }
private:
    Node* _root=nullptr;
};

 AVL 树的旋转

新节点插入到较高右子树的右侧---右右:左单旋

这里的A/B/C是这个树的子树,高度为h(h是可以=0的,这会是个伏笔)右子树高h+1,左子树高h,所以root结点的_bf为h+1-h=1。 同理,依次类推。

然后+了一个结点,变成这样了,平衡被打破,需要旋转。

旋转之后成了这样,这就是右右-左单旋的过程。 

代码实现 

	void LeftRotation(Node* root)
	{
		Node* son = root->_right;
		Node* sonleft = son->_left;

		root->_right = sonleft;
		if(sonleft)
		{ 
			sonleft->_parent = root;
		}
		
		Node* rootparent = parent->_parent;//这里所谓的根是相对而言的,
                                           //实际上的平衡因子的破坏一般是由局部到整体

		son->_left = root;
		root->_parent = son;

		if (rootparent == nullptr)
		{
			_root = son;
			_root->_parent = nullptr;
		}
		else
		{
			if (rootparent->_left == root)
			{
				rootparent->_left = son;
			}
			else
			{
				rootparent->_right = son;
			}
			son->_parent = rootparent;
		}
		root->_bf = son->_bf = 0;
	}

新节点插入到较高左子树的左侧---左左:右单旋 

然后左边的左子树新增了一个结点 

调整后就成了这样 了

代码实现:

	void  rightRotation(Node* root)
	{
		Node* son = root->_left;
		Node* sonright = son->_right;
		Node* rootparent = root->_parent;

		root->_left = sonright;
		if (sonright)
		{
			sonright->_parent = root;
		}

		son->_right = root;
		root->_parent = son;

		if (rootparent == nullptr)
		{
			_root = son;
			_root->_parent = nullptr;
		}
		else
		{
			if (rootparent->_left == root)
			{
				rootparent->_left = son;
			}
			else
			{
				rootparent->_right = son;
			}
			son->_parent = rootparent;
		}
		root->_bf = son->_bf = 0;
	}

 新节点插入较高左子树的右侧---左右:先左单旋再右单旋

这里有两种情况

一种是这样的,(子树高度h=0的情况)

新增结点后成了这样

还有另一种情况就是 这样

这里要说明一下,A和D子树高度为h,B和C子树的高度为h-1.然后新增了一个结点之后成了这样了

 然后进行调整。

当然还有一个在C的位置插入的,变化和上面差不多原理,只是最后son是-1,root是0,所以需要把son2的_bf值先记录下来。

代码实现 :

	void LeftandRightRotation(Node* root)
	{
		Node* son = root->_left;
		Node* son2 = son->_right;

		//先保存son2的平衡因子,需要在之后根据数字来对其他的进行平衡
        if(son2bf)
        {
            int son2bf = son2->_bf;
        }
        else
        {
            int son2bf=0;//将第一种情况单独列出来
        }

		LeftRotation(son);
		rightRotation(root);

		if (1 == son2bf)
		{
			son->_bf = -1;
		}
		else if (-1 == son2bf)
		{
			root->_bf = 1;
		}
	}

 新节点插入较高右子树的左侧---右左:先右单旋再左单旋

一开始和前面的类似,这里从新增结点的时候,平衡因子变化开始。同样的,B和C是高度为h-

1的子树,A和D是高度为h的子树。

然后进行调整

 

这里也有另一种情况,就是son2->_bf=1的情况 ,最后的结果也是root->_bf=-1,son->_bf=0,

也需要对son2的bf进行记录

还有一个情况是这样的,不可忽略,

代码实现如下:

	void RightandLeftRotation(Node* root)
	{
		Node* son = root->_right;
		Node* son2 = son->_left;

		if(son2bf)
        {
            int son2bf = son2->_bf;
        }
        else
        {
            int son2bf=0;//将特殊情况单独列出来
        }

		rightRotation(son);
		LeftRotation(root);

		if (1 == son2bf)
		{
			root->_bf = -1;
		}
		else if (-1 == son2bf)
		{
			son->_bf = 1;
		}
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值