AVL树(左,右双旋转)
我们依次插入数据结点56,90,67。最后形成了“折线”,所以我们要进行双旋转
我们依次插入数据结点78,34,45。最后形成了“折线”,所以我们要进行双旋转
我们要求的中序遍历是从小到大,旋转之后使得每个节点的平衡因子尽可能向0靠拢。
我们要借助左单旋转函数和右单旋转函数的能力来进行
我们把34和45,以45为基准,进行左单旋转,34放下来,45放上去,再以45位基准,把78进行右单旋转。
我们来看下面这个问题
(a)是平衡二叉树,我们可能在D或者F或者G插入数据,如果我们在D插入数据,D的高度由h变成h+1,我们回溯到B的时候,B的平衡因子由0变为-1,我们回溯到A,A的平衡因子由-1变成-2,我们的这3个结点在斜线上,进行右单旋转就OK了。
也有可能我们在F或者G插入数据,无论插入到F还是G底下,E的树高h,如果插入到的是G底下,E的高度变成h+1,回溯到B,B的平衡因子变为1,回溯到A,A的平衡因子变为-2。形成折线。我们以E为基准对B进行左旋,然后把A进行右旋。
我们再来看下面这张图
首先通过E节点进行转轴,对B进行左单旋转操作,变成c图。我们以E为基准点,对A进行右单旋转。最终的图形是d图。
如何知道进行1次右单旋转或者1次左单旋转或者1次左单旋转后再进行1次右单旋转?
如何判断这3个节点在一条直线上。如果是左平衡,节点的平衡因子从上到下依次为-2,-1,就在一条斜线上。(进行单旋转)
如果是下图,节点的平衡因子从上到下依次是-2,1,就是在一条折线上。(进行双旋转)
左单旋转的代码
void RotateLeft(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->rightchild;
newroot->parent = ptr->parent;// 1
ptr->rightchild = newroot->leftchild;
if (newroot->leftchild != nullptr)
{
newroot->leftchild->parent = ptr;// 2
}
newroot->leftchild = ptr;
if (ptr->parent == nullptr)
{
root = newroot;
}
else
{
if (ptr->parent->leftchild == ptr)
{
ptr->parent->leftchild = newroot;
}
else
{
ptr->parent->rightchild = newroot;
}
}
ptr->parent = newroot; // 3
}
右单旋转的代码
void RotateRight(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->leftchild;
newroot->parent = ptr->parent;//1
ptr->leftchild = newroot->rightchild;
if (newroot->rightchild != nullptr)
{
newroot->rightchild->parent = ptr;
}
newroot->rightchild = ptr;
if (ptr->parent == nullptr)
{
root = newroot;
}
else
{
if (ptr->parent->leftchild == ptr)
{
ptr->parent->leftchild = newroot;
}
else
{
ptr->parent->rightchild = newroot;
}
}
ptr->parent = newroot;
}
左平衡的程序代码
如果形成的是下图这种结构。
在A节点发生了不平衡,ptr指向A节点,在哪个节点发生了不平衡,就把哪个节点的指针给给ptr,我们定义2个指针。一个指向其左孩子,一个指向空。
case ==-1的情况
对B进行右单旋转
case==1的情况
情况:是跑到E这个地方插入了。
不管在F还是G插入,总会导致E的树高增高。导致B的平衡因子变成1,A的平衡因子变为-2。形成折线。我们要进行双旋转。
我们要进行先左后右的双旋转。
如果我们是在G插入节点,A的左子树要指向G,B的右子树要指向F,E的左边要指向B,E的右边要指向A,并且E作为根节点。E的平衡因子是1,A的配合因子是0,B的平衡因子是-1。
如果是嵌套的case的值是-1,则是在F节点插入节点。
E的平衡因子是0的可能性:
void LeftBalance(AVLTree& tree, AVLNode* ptr)//左平衡,对不平衡的AVL树进行左平衡
{
AVLNode* leftsub = ptr->leftchild, * rightsub = nullptr;
switch (leftsub->balance)
{
case 0: cout << "Left balance \n"; break;
case -1://如果是等于-1,是右单旋转
ptr->balance = 0;
leftsub->balance = 0;
RotateRight(tree, ptr);
break;
case 1://进行双旋转
rightsub = leftsub->rightchild;
switch (rightsub->balance)
{
case 1:
ptr->balance = 0;
leftsub->balance = -1;
break;
case 0:
ptr->balance = 0;
leftsub->balance = 0;
break;
case -1:
ptr->balance = 1;
leftsub->balance = 0;
break;
}
rightsub->balance = 0;
RotateLeft(tree,leftsub);
RotateRight(tree,ptr);
break;
}
}
右平衡的程序代码
首先进行左单旋转
void RightBalance(AVLTree& tree, AVLNode* ptr)//右平衡
{
AVLNode* rightsub = ptr->rightchild, * leftsub = nullptr;
switch (rightsub->balance)
{
case 0: cout << "right balance \n"; break;
case 1://左单旋转
ptr->balance = 0;
rightsub->balance = 0;
RotateLeft(tree, ptr);
break;
case -1://双旋转
leftsub = rightsub->leftchild;
switch (leftsub->balance)
{
case 1:
ptr->balance = -1;
rightsub->balance = 0;
break;
case 0:
ptr->balance = 0;
rightsub->balance = 0;
break;
case -1:
ptr->balance = 0;
rightsub->balance = 1;
break;
}
leftsub->balance = 0;
RotateRight(tree, rightsub);
RotateLeft(tree, ptr);
break;
}
}
插入数据
bool Insert(AVLTree& tree, KeyType kx)//插入
{
Adjust_AVL(tree, p);
return true;
}
进行调整
在E插入一个数据130
如果我们插入一个67,然后插入34,然后插入100。
则不需要调整。
如果我们再插入23,45
高度不变,不需要回溯。
如果我们再插入120。90。也不需要回溯
我们再插入130和140。需要进行旋转,旋转后,不需要回溯
调整后,不会使得平衡因子超过2。
一旦调用平衡函数,高度不会发生变化。
void Adjust_AVL(AVLTree& tree, AVLNode* ptr)//调整AVL树
{
AVLNode* pa = ptr->parent;//指向双亲
bool high = true;//高度初始化为真
while (high && pa != nullptr)//还没到根节点
{
if (pa->rightchild == ptr)//ptr插入到右子树
{
switch (pa->balance)//看平衡因子
{
case 0: pa->balance = 1; break;//右边插入,变为1
case -1:
//右边低 左边高,给右边插入
pa->balance = 0;
high = false;//高度不变,不需要回溯
break;
case 1:
//右边高 ,左边低,给右边插入,调用右平衡
RightBalance(tree,pa);
high = false;//高度不变,不需要回溯
break;
}
}
else //在双亲的左边
{
switch (pa->balance)
{
case 0: pa->balance = -1; break;//在左边插入,变为-1
case 1: //右边高左边低,在左边插入
pa->balance = 0;
high = false;//高度不变,不需要回溯
break;
case -1:
//右边低左边高,在左边插入
LeftBalance(tree, pa);//调动左平衡
high = false;//高度不变,不需要回溯
break;
}
}
ptr = pa;
pa = ptr->parent;//回溯
}
}