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