AVL树的基本实现

       在学习二叉树后我们知道它存在缺陷,就是当我们插入一些有序节点时就基本退化为链表的形式,达不到我们想要的结果,因此,在这里为了解决这个问题就引出了平衡二叉树(AVL)的概念。

AVL树又称为高度平衡的二叉搜索树,它能保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均搜索长度,那它具有哪些性质呢?

1.左右子树的高度差的绝对值不超过1;

2.树中的每个左子树和右子树都是AVL树;

3. 每个节点都有一个平衡因子(balance factor--bf),任一节点的平衡因子是-1,0,1。(每个节点的平衡因子等于右子树的高度减去左子树的高度 ).

提到平衡因子只有-1,1,0这几种限制,就引来了旋转问题,旋转的目的是什么呢?它在于在某种情况下将其高度降下来,从而满足AVL的性质特点。

旋转一般分为四种情况:

1.左单旋;

2.右单旋.(原理类似)





3.左右双旋;

4.右左双旋.

这两种当然可以直接调用上面的两种单旋方式,顾名思义,左右双旋是先调用左单旋再右单旋;右左双旋是先调用右单旋再左单旋。但是这两种双旋不能只是单单的调用单旋就能解决的,在某种特殊的情况下,调用单旋后将平衡因子都改为0是不对的,具体看如下图分析:




分析就到这,下来是插入节点的代码:

template<class K,class V>
bool AVLTree<K,V>::Insert(const K& key,const V& value)
{
	//以key值为标准,key值相同插入不成功
	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,与parent连接上
	cur=new Node(key,value);
	if (parent->_key < key)
	{
		parent->_right=cur;
		cur->_parent=parent;
	}
	else if (parent->_key > key)
	{
		parent->_left=cur;
		cur->_parent=parent;
	}

	//检查是否平衡,不平衡进行调整
	parent=cur->_parent;
	while (parent)
	{
		//先判断cur添加的位置,从而确定怎样更改parent->_bf
		if (parent->_left==cur)
		{
			parent->_bf--;
		}
		if (parent->_right==cur)
		{
			parent->_bf++;
		}

		if (parent->_bf==0)
		{
			break;
		}
		else if (parent->_bf==1 || parent->_bf==-1) //树的高度变了,继续往上调整
		{                                      
			cur=parent;
			parent=cur->_parent;
		}
		else   //parent->_bf==2 || parent->_bf==-2
		{
			if (parent->_bf==2)
			{
				if (cur->_bf==1)  //需要左单旋
					_RotateL(parent);
				else
					_RotateRL(parent); //右左双旋
			}
			else if (parent->_bf==-2)
			{
				if (parent->_bf==-2)
				{
					if (cur->_bf==-1)  //需要右单旋
						_RotateR(parent);
					else
						_RotateLR(parent); //左右双旋
				}
			}
		}
	}
}

下来是四种旋转的实现:

template<class K,class V>
void AVLTree<K,V>::_RotateL(Node*& parent)
{
	Node* subR=parent->_right;
	Node* subRL=subR->_left;
	Node* ppNode=parent->_parent;
	subR->_left=parent;
	parent->_right=subRL;
	if(subRL)
		subRL->_parent=parent;
	parent->_parent=subR;
	if (ppNode==NULL)
	{
		_root=subR;
		subR->_parent=NULL;
	}
	else
	{
		if (ppNode->_right==parent)
			ppNode->_right=subR;
		else
			ppNode->_left=subR;

		subR->_parent=ppNode;
	}
	subR->_bf=parent->_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 (ppNode==NULL)
	{
		_root=subL;
		subL->_parent=NULL;
	}
	else
	{
		if (ppNode->_left==parent)
			ppNode->_left=subL;
		else
			ppNode->_right=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)  //新增节点
	{
		parent->_bf=subL->_bf=subLR->_bf=0;
	}
	else if(bf==-1)  //b处插入
	{  
		parent->_bf=1;
		subL->_bf=0;
		subLR->_bf=0;
	}
	else           //c处插入
	{
		parent->_bf=0;
		subL->_bf=-1;
		subLR->_bf=1;
	}
}

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)  //新增节点
	{
		parent->_bf=subR->_bf=subRL->_bf=0;
	}
	else if(bf==-1)  //b处插入
	{  
		parent->_bf=0;
	    subR->_bf=1;
		subRL->_bf=0;
	}
	else           //c处插入
	{
		parent->_bf=-1;
		subR->_bf=0;
		subRL->_bf=1;
	}
}

在AVL树中还有个问题很重要,就是如何判断其是否满足高度平衡的条件?

首先,是否平衡是由平衡因子决定的,而平衡因子的求解是:右子树高度-左子树高度;所以我们必须将左右子树的高度求解出来,看如下代码:

template<class K,class V>
bool AVLTree<K,V>::_Isbalance(Node* root)
{
	if (root==NULL)
		return true;
	size_t LeftHeight=_Height(root->_left);
	size_t RightHeight=_Height(root->_right);
	int bf=RightHeight-LeftHeight;
	if (root->_bf!=bf)
	{
		cout<<"Unbalance:"<<root->_key<<endl;
		return false;
	}
	return abs(bf)<2 && _Isbalance(root->_left) 
		&& _Isbalance(root->_right);
}


这种方法可以是可以,但是每次都要将当前节点的左右子树高度求出来,时间复杂度是相对较高的,最坏情况下为O(N^2).那么该如何优化呢?

刚才我们提到,每次都得将当前节点的左右子树高度求出来,那么我们是不是可以在判断该节点的同时将其高度返回去,这样遍历一次就可以了,时间复杂度自然就降为O(n).

思路:采取后序遍历的方式,先判断左节点,合法则高度加1,然后往上一层递归返回,再进行右节点的计算,最后再进行根节点的计算。在后序遍历中,通过传递引用的方式将子树的高度传到根节点处去。


template<class K,class V>
bool AVLTree<K,V>::_IsbalanceOP(Node* root,size_t& Height)
{
	if (root==NULL)
	{
		Height=0;
		return true;
	}
	size_t LeftHeight=0;
	size_t RightHeight=0;
	if (_IsbalanceOP(root->_left,LeftHeight)==false)
		return false;
	if(_IsbalanceOP(root->_right,RightHeight)==false)
		return false;
	//表示以当前节点为根节点的左右子树是平衡的
	int bf=RightHeight-LeftHeight;
	if (abs(bf)<2)
	{
		Height=1+(RightHeight > LeftHeight ? RightHeight : LeftHeight);
		return true;
	}
}


我将完整代码托管到github上:AVLTree


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值