在讲解AVL树之前必须了解二叉搜索树,
可以看我之前的博客:二叉搜索树
AVL树是在二叉搜索树的基础上,在向二叉树排序树中插入新的结点,如果保证每个结点的左右子树的高度差的绝对值不超过1,即需要在插入的时候判断是否满足条件,然后对树进行旋转,从而可减低树的高度,减少平均搜索长度。
AVL树:(1)它的左右子树都是AVL树
(2)右子树和左子树的高度差(平衡因子)的绝对值不能超过1 (1 0 -1)。
给出实现AVL树的节点
template<class T>
struct BlancedBianryTreeNode
{
BlancedBianryTreeNode(T data)
: _data(data)
, _bf(0)
, _pLeft(NULL)
, _pRight(NULL)
, _pParent(NULL)
{}
T _data;
int _bf; //平衡因子:右子树的高度-左子树的高度
BlancedBianryTreeNode<T>* _pLeft;
BlancedBianryTreeNode<T>* _pRight;
BlancedBianryTreeNode<T>* _pParent;
};
下面讲解如何向平衡二叉树中插入一个元素, 大神勿喷。
步骤1:如果是空树,插入后即为根节点,插入后直接返回
步骤2:如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回(注:与二叉搜索树的插入操作一样)
步骤3:插入结点,链接到树上,
步骤4:更新平衡因子
步骤5:对树进行调整:旋转
步骤2详解:在找的过程中,需要标记其找入插入位置的父节点pParent,因为在插入新节点pCur的时候,需要判断插入的位置(插入到父节点的左边还是右边)。直接看下面的代码
步骤4详解:插入新元素后该元素的平衡因子为0,但是其父节点的平衡因子可能改变,所以需要更新其平衡因子bf。
如果pCur为父节点pParent的左孩子,则pParent->_bf–;
如果pCur为父节点pParent的右孩子,则pParent->_bf++;
一直向上更新,直到更新到根节点或者父节点的bf为0。
如果父节点的bf为2或者-2的话,就不保持平衡,需要进行旋转。
步骤5详解 旋转有四种旋转方式:左旋,右旋,左右旋,右左旋
(1)左旋分为三种情况:
步骤一:先把SubRL链接到pParent上,更新SubRL的父节点,注意SubRL可能为空
步骤二:修改subR,挂到原子树上,注意先得保存pParent的父节点,注意pParent可能为根节点,其父节点为NULL
步骤三:修改pParent,挂接到subR上
步骤四:更新pParent和SubR的平衡因子
从图中可以看出:三种方式的左旋后pParent和SubR的平衡因子都为0。所以旋转后,再更新pParent和SubR的平衡因子。
什么时候左旋:pParent->bf 为2 ,SubR->bf 为1。
代码
void _RotateL(Node* pParent) //左旋
{
Node* SubR = pParent->_pRight; //当前节点pParent的左孩子
Node* SubRL = SubR->_pLeft; //可能为空
//先将 SubRL 链接到
pParent->_pRight = SubRL;
if (SubRL) //第三种情况
{
SubRL->_pParent = pParent;
}
//修改SubR
Node *pPparent = pParent->_pParent; //pParent可能为根节点
SubR->_pParent = pPparent; //修改SubR的父节点
//把SubR链接到原子树上
if (pPparent == NULL)//可能为根节点
{
_pRoot = SubR;
}
else
{
if (pParent == pPparent->_pLeft)
pPparent->_pLeft = SubR;
else
pPparent->_pRight = SubR;
}
//修改pParent,
SubR->_pLeft = pParent;
pParent->_pParent = SubR;
//更新SubR和pParent的平衡因子
SubR->_bf = pParent->_bf = 0;
}
(2)右旋:和左旋相似
直接贴代码:步骤和左旋类似
什么时候左旋:pParent->bf 为-2 ,SubL->bf 为-1
void _RotateR(Node* pParent) //右旋
{
Node* SubL = pParent->_pLeft;
Node* SubLR = SubL->_pRight; //可能为第三种情况
//修改SubLR
pParent->_pLeft = SubLR;
if (SubLR)
{
SubLR->_pParent = pParent;
}
//修改subL
Node* pPparent = pParent->_pParent;
SubL->_pParent = pPparent;
//subL链接到原子树上
if (pPparent == NULL)
_pRoot = SubL;
else
{
if (pPparent->_pLeft == pParent)
pPparent->_pLeft = SubL;
else
pPparent->_pRight = SubL;
}
//修改pparent
SubL->_pRight = pParent;
pParent->_pParent = SubL;
//更新subl和pparent的平衡因子
SubL->_bf = pParent->_bf = 0;
}
(3)左右旋:先进行左旋,再进行右旋
分析每种情况的pParent和SubL和SubLR的平衡因子,
找到规律,当未更新subLR->bf = -1时,
pParent->bf = 1; SubL->bf = 0;
当未更新subLR->bf = 1时,pParent->bf = 0; SubL->bf = -1;
**左右旋的时机:**pParent->bf 为-2 ,SubR->bf 为1
void _RotateLR(Node* pParent) //左右旋 分三种
{
//根据 subLR的平衡因子:
int bs = pParent->_pLeft->_pRight->_bf;
Node* SubL = pParent->_pLeft;
_RotateL(pParent->_pLeft);
_RotateR(pParent);
if (bs == 1)
{
pParent->_bf = 0;
SubL->_bf = -1;
}
else if (bs == -1)
{
pParent->_bf = 1;
SubL->_bf = 0;
}
(4)右左旋:与左右旋类似,
分析每种情况的pParent和SubR和SubRL的平衡因子,与左旋旋正好相反
找到规律,当未更新subRL->bf = -1时,
pParent->bf = 0; SubR->bf = 1;
当未更新subRL->bf = 1时,pParent->bf = -1; SubR->bf = 0;
右左旋时机:pParent->bf 为2 ,SubR->bf 为-1
void _RotateRL(Node* pParent) //右左旋转
{
int bs = pParent->_pRight->_pLeft->_bf;
Node* SubR = pParent->_pRight;
_RotateR(pParent->_pRight);
_RotateL(pParent);
if (bs == 1)
{
pParent->_bf = -1;
SubR->_bf = 0;
}
else if (bs == -1)
{
pParent->_bf = 0;
SubR->_bf = 1;
}
}
插入操作的整体代码:
bool Insert(T data)
{
if (_pRoot == NULL)
{
_pRoot = new Node(data);
return true;
}
Node* pCur = _pRoot;
Node* pParent = NULL;
while (pCur)
{
if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (data > pCur->_data)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
return false;
}
//找到插入节点,
pCur = new Node(data);
pCur->_pParent = pParent;
if (data > pParent->_data)
pParent->_pRight = pCur;
else
pParent->_pLeft = pCur;
//更新平衡因子
while (pParent)
{
if (pCur == pParent->_pLeft)
pParent->_bf--;
else
pParent->_bf++;
//平衡因子改变
if (pParent->_bf == 0)
return true;
else if (pParent->_bf <-1 || pParent->_bf > 1)
break;
else
{
pCur = pParent;
pParent = pCur->_pParent;
}
}
//跳出循环 pParent的因子可能为2 或者 -2 则进行旋转
//旋转
//可能pParent为空,不需要进行旋转
if (pParent)
{
if (pParent->_bf == 2)
{
if (pCur->_bf == 1)//进行左旋
_RotateL(pParent);
else
_RotateRL(pParent);
}
else if (pParent->_bf == -2)
{
if (pCur->_bf == -1)
_RotateR(pParent);
else
_RotateLR(pParent);
}
}
return true;
}
如果有错误,欢迎大家指出来。