思维导图
AVL树的平衡调整
四种类型
LL
LR
代码演示
代码
/*************************************************************************
> File Name: avl.cpp
> Mail: 1136984246@qq.com
************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#define L(n) (n->lchild)
#define R(n) (n->rchild)
#define H(n) (n->h)
using namespace std;
typedef struct Node {
int key, h; // 插入和删除注意调整树高字段, 树高h是AVL树的精髓
struct Node *lchild, *rchild;
} Node;
// 引入虚拟空节点NIL(NULL Is Legal)字段,替代NULL, NULL不可以访问, NIL是实际节点,可访问; 对于红黑树很方便
Node __NIL;
#define NIL (&__NIL)
__attribute__((constructor)) // 构造函数:提前于主函数执行
void init_NIL() {
NIL->key = 0, NIL->h = 0;
NIL->lchild = NIL->rchild = NIL;
}
// 初始化
Node *getNewNode (int key) {
Node *p = (Node *)malloc(sizeof(Node));
p->key = key;
p->h = 1;
p->lchild = p->rchild = NIL; // 左右子节点都指向NIL
return p;
}
// 更新树高
void update_height(Node *root) {
root->h = (H(L(root)) > H(R(root)) ? H(L(root)) : H(R(root))) + 1; // max(左,右) + 1
}
// 左旋操作 :
Node *left_rotate(Node *root) {
Node *temp = root->rchild; // 技巧:定义temp,记录根的右子节点 -> 新的根节点是temp
root->rchild = temp->lchild; // 将temp左子树[挂到]旧的根节点root的右边[root右边和temp失去连接]
temp->lchild = root; // 先更新temp的左子树为旧的根节点的root[temp和原来的左子树失去连接]
update_height(root); // 先更新root,再更新temp[因为root已经在底下了]
update_height(temp); // 在小左旋是,树高可能会改变
return temp; // 返回新的根节点
}
// 右旋操作:与左旋进行对称
Node *right_rotate(Node *root) {
Node *temp = root->lchild;
root->lchild = temp->rchild;
temp->rchild = root;
update_height(root);
update_height(temp);
return temp;
}
// 平衡调整:返回Node*,同样可能改变根节点的地址
Node *maintain(Node *root) {
if (abs(H(L(root)) - H(R(root))) <= 1) return root; // 平衡,不需要调整
if (root->lchild->h > root->rchild->h) {
// 从[L]开始
// 判断是不是LR
if (root->lchild->lchild->h < root->lchild->rchild->h) {
root->lchild = left_rotate(root->lchild);
}
root = right_rotate(root); // LL, LR都需要大右旋
} else {
// 从[R]开始
// 判断是不是RL
if (root->rchild->rchild->h < root->rchild->lchild->h) {
root->rchild = right_rotate(root->rchild);
}
root = left_rotate(root); // RR, RL都需要大左旋
}
return root;
}
// 增:插入 + 调整; 返回;返回Node*,因为根节点有可能发生改变
Node *insert(Node *root, int key) {
if (root == NIL) return getNewNode(key); // 插入操作
if (root->key == key) return root;
if (key < root->key) {
root->lchild = insert(root->lchild, key);
} else {
root->rchild = insert(root->rchild, key);
}
update_height(root); // 维护树高
return maintain(root); // 回溯时,进行平衡调整
}
// 找前驱:仅仅针对度为2的节点
Node *predecessor(Node *root) {
Node *temp = root->lchild;
while (temp->rchild != NIL) temp = temp->rchild;
return temp;
}
// 删
Node *erase(Node *root, int key) {
if (root == NIL) return NIL;
if (key < root->key) {
root->lchild = erase(root->lchild, key);
} else if (key > root->key) {
root->rchild = erase(root->rchild, key);
} else {
if (root->lchild == NIL ||root->rchild == NIL) {
Node *temp = root->lchild ? root->lchild : root->rchild;
free(root);
return temp; // 此处不需要平衡调整,temp都没有子节点
} else {
// 找前驱,替换,删前驱
Node *temp = predecessor(root);
root->key = temp->key;
root->lchild = erase(root->lchild, temp->key);
}
}
update_height(root);
return maintain(root);
}
// 销毁
void clear(Node *root) {
if (root == NIL) return;
clear(root->lchild);
clear(root->rchild);
free(root);
return;
}
// 前序遍历:方便画树
void print(Node *root) {
printf("%d[%d], %d, %d)\n",
root->key, root->h,
root->lchild->key,
root->rchild->key
);
return;
}
void output(Node *root) {
if (root == NIL) return ;
print(root);
output(root->lchild);
output(root->rchild);
return;
}
int main() {
int op, val;
Node *root = NIL;
while (~scanf("%d%d", &op, &val)) {
switch(op) {
case 0: root = erase(root, val); break;
case 1: root = insert(root, val); break;
}
if (op == 0) printf("erase %d\n", val);
else if (op == 1) printf("insert %d\n", val);
output(root);
printf("-----------------------\n");
}
return 0;
}
测试结果