文章目录
一、AVL树的相关概念
1.AVL树
2.平衡因子
二、AVL树的基本结构
typedef int KeyType;
typedef struct AVLNode {
KeyType key;
AVLNode* leftchild;
AVLNode* parent;
AVLNode* rightchild;
int balance; //平衡因子
}AVLNode, * AVLTree;
三、AVL树的平衡化处理
什么是平衡化处理?为什么平衡化处理?
在向一棵本来是高度平衡(0,1,-1)的AVL树中插入一个新结点和在BST树一样,区别是,如果树中某个结点的平衡因子的绝对值|balance| > 1,则出现了不平衡,需要做平衡化处理,I使得树中各结点重新平衡化。
1.左旋
①描述
②算法思想
(1)如上图所示,改变各个结点的子指针。定义newroot = ptr->rightchild;ptr->rightchild = newroot->leftchild; newroot->leftchild = ptr;
(2)改变各节点的的父指针。从下往上改。先修改60的,如果newroot的左孩子(60)存在就将它的父指针指向ptr,如果ptr的父指针为空,即ptr就是root,直接将root = newroot;然后再判断ptr是ptr->parent的那个结点,最后改变ptr的父指针。
其实各个步骤是紧密联系在一块的。在写代码的时候先写出上图的三步,写出三行代码(1),在(1)的每行的后面添加(2)的代码。
③代码展示:
void RotateLeft(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->rightchild;
newroot->parent = ptr->parent;
ptr->rightchild = newroot->leftchild;
if (newroot->leftchild != nullptr)
{
newroot->leftchild->parent = ptr;
}
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;
}
2.右旋
①描述: 和左旋是相反的,对称的。
②代码展示
void RotateRight(AVLTree& root, AVLNode* ptr)
{
AVLNode* newroot = ptr->leftchild;
newroot->parent = ptr->parent;
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;
}
3.双旋
**描述:**双旋是指先左后右,或者先右后左,调用左旋右旋就可以了。
左平衡处理
**①描述:**左平衡处理是指,由于在左子树上插入数据导致的AVL树发生的失衡的处理。分为右单旋和双旋(先左旋后右旋)。如下图。
②算法描述:
(1)关于变量:ptr为发生不平衡的结点 leftsub为他的左节点。
(2)我们先判断leftsub的平衡因子来判断是在哪个子树上插入的数据。如果balance0,说明是出于平衡状态的。如果balance-1,说明是在左子树上插入的,这是一条直线,调整旋转后的平衡因子,然后右旋就可以了。如果balance == 1,说明在右子树上插入的,判断在rightsub的平衡因子,再进行调整平衡因子,然后执行相应的旋转就可以了。
(3)关于rightsub->balance == 0的特殊情况。
关于如何确定调整之后的平衡因子:
③代码展示:
void LeftBalance(AVLTree& tree, AVLNode* ptr)
{
//在哪发生不平衡,ptr就是哪个结点
AVLNode* leftsub = ptr->leftchild;
AVLNode* rightsub = nullptr;
switch (leftsub->balance)
{
case 0:
cout << "已经平衡\n"; break;
case -1: //直线
leftsub->balance = 0;
ptr->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; //rightsub的平衡因子最后一定为0
RotateLeft(tree, leftsub);
RotateRight(tree, ptr);
break;
}
}
右平衡处理
描述:
和左平衡处理相反。
②代码展示:
void RightBalance(AVLTree& tree, AVLNode* ptr)
{
AVLNode* rightsub = ptr->rightchild;
AVLNode* leftsub = nullptr;
switch (rightsub->balance)
{
case 0:
cout << "已经平衡\n"; break;
case 1: //直线,在右子树上插入
ptr->balance = 0;
rightsub->balance = 0;
RotateLeft(tree, ptr);
break;
case -1:
leftsub = rightsub->leftchild;
switch (leftsub->balance)
{
case 1:
rightsub->balance = 0;
ptr->balance = -1;
break;
case 0:
rightsub->balance = 0;
ptr->balance = 0;
break;
case -1:
rightsub->balance = 1;
ptr->balance = 0;
}
leftsub->balance = 0;
RotateRight(tree, rightsub);
RotateLeft(tree, ptr);
break;
}
}
四、AVL树的调整
①描述:
根据插入的情况对AVL树的平衡因子进行相应的调整,不平衡时进行相应的旋转。
②算法思想:
定义一个父节点,循环直到父节点为空。进行判断是否需要进行调整。每次只看ptr是父节点的哪个孩子,然后再判断是否需要调整。如果添加之后(或者经过调整之后),父节点的平衡因子等于0,就不需要在往下进行循环判断了。
③代码展示:
void Adjust_AVL(AVLTree& tree, AVLNode* ptr)
{
AVLNode* pa = ptr->parent;
bool high = true;
while (high && pa != nullptr)
{
if (pa->leftchild == ptr) //在左边插入
{
switch (pa->balance)
{
case 0: //原来pa的平衡因子为0,在左边插入之后-1
pa->balance = -1;
break;
case -1:
LeftBalance(tree, pa); //-1之后变成-2,需要调整
high = false; //平衡之后就不用再判断了
break;
case 1:
pa->balance = 0;
high = false; //并没有改变树的高度,插入之后变得更平衡了,所以不用再往下判断了
break;
}
}
else
{
switch(pa->balance)
{
case 0:
pa->balance = 1;
break;
case -1:
pa->balance = 0;
high = false;
break;
case 1:
RightBalance(tree, pa);
high = false;
break;
}
}
ptr = pa;
pa = ptr->parent;
}
}
五、AVL树的插入
①算法思想:
和普通二叉树插入的思想类似,每次插入一个结点都要对这个结点进行一次调整。传入的指针就是新插入的结点。
②代码展示:
void Insert(AVLTree& root, KeyType val)
{
AVLNode* p = root;
AVLNode* pa = nullptr;
AVLNode* newnode = BuyNode(val);
assert(newnode != nullptr);
if (p == nullptr)
{
root = newnode;
newnode->parent = nullptr;
return;
}
while (p != nullptr )
{
if (p->key == val)
{
free(newnode);
return;
}
pa = p;
p = p->key > val ? p->leftchild : p->rightchild;
}
newnode->parent = pa;
if (val > pa->key)
{
pa->rightchild = newnode;
}
else
{
pa->leftchild = newnode;
}
Adjust_AVL(root, newnode); //对插入的结点进行调整
}
测试
void AVL_InOrder(AVLNode* tree)
{
AVLNode* p = tree;
if (tree == nullptr)
{
return;
}
AVL_InOrder(p->leftchild);
cout << p->key << " ";
AVL_InOrder(p->rightchild);
}
void AVL_PreOrder(AVLNode* tree)
{
AVLNode* p = tree;
if (tree == nullptr)
{
return;
}
cout << p->key << " ";
AVL_PreOrder(p->leftchild);
AVL_PreOrder(p->rightchild);
}
int main()
{
vector<int> vc = { 56,45,78,67,89,76};
AVLTree tree = nullptr;
for (auto& x : vc)
{
Insert(tree, x);
}
AVL_InOrder(tree); cout << endl;
AVL_PreOrder(tree); cout << endl;
return 0;
}
结果如下: