当连续并且有序的数据陆续插入一棵二叉搜索树( binary search tree )时,这时树就会退化为链表,查找、插入和删除都需要花费 O ( n ) O(n) O(n) 时间,这时二叉搜索树的优势就没有了,AVL 树的发明就是为了解决这种问题。
在计算机科学中,AVL 树是一种自平衡的二叉搜索树。在 AVL 树中,任何节点的两个子树的高度最多相差一,如果在插入或删除时,它们相差大于一时,会进行自平衡以恢复此属性。在平均和最坏情况下,查找、插入和删除都需要 O ( l o g n ) O(log n) O(logn) 时间。
AVL 树是以两位前苏联发明者 Georgy Adelson-Velsky 和 Evgenii Landis 的名字命名的,他们在1962年的论文《信息组织算法》中发表了这棵树。
一棵 AVL 树的节点,首先是一个二叉搜索树的节点,这表示节点中必须有数据,左子树和右子树,并且数据必须是可比较的,因为二叉搜索树规定了,父节点的数据一定大于左子树,一定小于右子树。
由于 AVL 树需要计算树的高度用于判断是否平衡,为了减少重复计算,所以节点中需要再保存一个高度(这里定义:叶子节点的高度为1)。而一般介绍 AVL 树的文章中都会有一个平衡因子( Balance Factor ),BF是左右子树的高度之差,这里定义
B F ( x ) = g e t H e i g h t ( l e f t ( x ) ) − g e t H e i g h t ( r i g h t ( x ) ) BF(x) = getHeight(left(x)) - getHeight(right(x)) BF(x)=getHeight(left(x))−getHeight(right(x))
x x x 为要计算平衡因子的节点, l e f t ( x ) left(x) left(x)为 x x x 的左子树, r i g h t ( x ) right(x) right(x)为 x x x 的右子树。有了高度值,很容易计算平衡因子,这里就不保存平衡因子了。
为了方便理解,这里规定,AVL 树不存储重复值,这表示 AVL 树在插入时不会插入相同的数据。
下面是 main.h ,定义了一些 AVL 树需要用到的数据结构和方法。
#include
#include
#define MAX(A, B) (A > B ? A : B)
struct _treeNode {
int data; // 节点存储的数据
int height; // 节点的高度
struct _treeNode *left; // 节点的左子树
struct _treeNode *right; // 节点的右子树
};
typedef struct _treeNode* treeNode;
struct _avlTree {
treeNode root; // 树的根节点
int size; // 树的节点个数
int height; // 树的高度
};
typedef struct _avlTree* avlTree;
void avlTreeInit(avlTree* tree);
void avlTreeInsert(avlTree tree, int val);
void avlTreeDelete(avlTree tree, int val);
treeNode avlTreeSearch(avlTree tree, int val);
treeNode maxNode(treeNode node);
treeNode minNode(treeNode node);
int getHeight(treeNode root); // 获取节点的高度
void calHeight(treeNode root); // 计算节点的高度
int calBf(treeNode node); // 计算平衡因子
void printTree(avlTree tree); //先序遍历
void printTreeByLevel(avlTree tree); //层次遍历
#include "main.h"
void avlTreeInit(avlTree* tree) {
*tree = (avlTree) malloc(sizeof(struct _avlTree));
(*tree)->root = NULL;
(*tree)->size = 0;
(*tree)->height = 0;
}
int getHeight(treeNode root) {
if (!root) return 0;
return root->height;
}
void calHeight(treeNode root) {
if (!root) return;
root->height = MAX(getHeight(root->left), getHeight(root->right)) + 1;
}
int calBf(treeNode node) {
return getHeight(node->left) - getHeight(node->right);
}
treeNode maxNode(treeNode node) {
treeNode max = node;
while (node) {
max = node;
node = node->right;
}
return max;
}
treeNode minNode(treeNode node) {
treeNode min = node;
while (node) {
min = node;
node = node->left;
}
return min;
}
void printNode(treeNode node) {
if (node) {
printNode(node->left);
printf("%d ", node->data);
printNode(node->right);
}
}
void printTree(avlTree tree) {
printf("tree size:%d\n", tree->size);
printNode(tree->root);
printf("\n");
}
void printTreeByLevel(avlTree tree) {
if (!tree || !tree->root) return;
treeNode * arr = (treeNode *) malloc(tree->size * sizeof(treeNode));
int left = 0, right = 1;
int count = 0;
arr[count++] = tree->root;
while (left < right) {
for (int i = left; i < right; i++) {
printf("[val:%d, height:%d] ", arr[i]->data, arr[i]->height);
if (arr[i]->left) {
arr[count++] = arr[i]->left;
}
if (arr[i]->right) {
arr[count++] = arr[i]->right;
}
left++;
}
right = count;
printf("\n");
}
}
当插入或删除节点时,需要从插入的位置沿向根的路径回溯,检查各节点平衡因子。如果出现 BF 为2或-2的情况,这时就需要自平衡。AVL 树通过旋转(AVL 树的重点) 来实现自平衡。
旋转的类型可以根据旋转次数分为分为:单旋或双旋。单旋表示1次旋转,双旋表示2次旋转
总而言之,旋转一共有四种:LL、RR、LR、RL
LL
LL:LeftLeft,也称为"左左"。插入或删除一个节点后,向上回溯到某个节点(失衡节点)的BF为2,并且插入节点还位于失衡节点的左节点的左子树
举个栗子:向下面的 AVL 树插入5后,节点9进行自平衡,LL旋转

treeNode rotateLL(treeNode node) {
treeNode left = node->left;
node->left = left->right;
left->right = node;
calHeight(node);
calHeight(left);
return left;
}
RR
RR:RightRight,也称为"右右"。插入或删除一个节点后,向上回溯到某个节点(失衡节点)的BF为-2,并且插入节点还位于失衡节点的右节点的右子树
举个栗子:向下面的 AVL 树插入6后,节点2进行自平衡,RR旋转

treeNode rotateRR(treeNode node) {
treeNode right = node->right;
node->right = right->left;
right->left = node;
calHeight(node);
calHeight(right);
return right;
}
LR
LR, LeftRight,也称为"左右"。插入或删除一个节点后,向上回溯到某个节点(失衡节点)的BF为2, 并且插入节点还位于失衡节点的左节点的右子树。
举个栗子:向下面的 AVL 树插入5后,节点7进行自平衡LR旋转,先将左节点4 RR旋转,再将自身LL旋转

treeNode rotateLR(treeNode node) {
node->left = rotateRR(node->left);
return rotateLL(node);
}
RL
RL, RightLeft,也称为"右左"。插入或删除一个节点后,向上回溯到某个节点(失衡节点)的BF为-2, 并且插入节点还位于失衡节点的右节点的左子树。
举个栗子:向下面的 AVL 树插入8后,节点7进行自平衡LR旋转,先将右节点12 LL旋转,再将自身RR旋转

treeNode rotateRL(treeNode node) {
node->right = rotateLL(node->right);
return rotateRR(node);
}
下图以插入[3, 2, 1, 4, 5, 6, 7, 16, 15, 14, 13, 12, 11, 10, 8, 9]为例,演示 AVL 树插入过程
treeNode avlTreeNodeInsert(avlTree tree, treeNode root, int val) {
if (!root) {
treeNode v = (treeNode)calloc(1, sizeof(struct _treeNode));
v->data = val;
v->height = 1;
tree->size++;
return v;
}
if (root->data > val) {
root->left = avlTreeNodeInsert(tree, root->left, val);
if (calBf(root) == 2) {
if (root->left->data > val) {
root = rotateLL(root);
} else {
root = rotateLR(root);
}
}
} else if (root->data < val) {
root->right = avlTreeNodeInsert(tree, root->right, val);
if (calBf(root) == -2) {
if (root->right->data < val) {
root = rotateRR(root);
} else {
root = rotateRL(root);
}
}
}
calHeight(root);
return root;
}
void avlTreeInsert(avlTree tree, int val) {
treeNode n = avlTreeNodeInsert(tree, tree->root, val);
tree->root = n;
tree->height = getHeight(tree->root);
}
1. 如果删除节点是叶子节点,那很简单,直接删掉即可。
2. 如果删除节点只有左节点或有节点,那也很简单,将子节点替换为删除节点
3. 如果被删除节点有左右子节点,需要根据左右子树高度判断,左子树高就将左子树最大值跟被删节点调换,然后删除,右子树高的话就将右子树中最小值调换然后删除,这样做的话树仍是平衡的
treeNode avlTreeNodeDelete(avlTree tree, treeNode root, int val) {
if (root) {
if (root->data > val) {
root->left = avlTreeNodeDelete(tree, root->left, val);
if (calBf(root) == -2) {
if (getHeight(root->right->left) > getHeight(root->right->right)) {
root = rotateRL(root);
} else {
root = rotateRR(root);
}
}
} else if (root->data < val) {
root->right = avlTreeNodeDelete(tree, root->right, val);
if (calBf(root) == 2) {
if (getHeight(root->left->left) > getHeight(root->left->right)) {
root = rotateLL(root);
} else {
root = rotateLR(root);
}
}
} else {
if (root->left && root->right) {
if (getHeight(root->left) > getHeight(root->right)){
treeNode max = maxNode(root->left);
root->data = max->data;
root->left = avlTreeNodeDelete(tree, root->left, max->data);
} else{
treeNode min = minNode(root->right);
root->data = min->data;
root->right = avlTreeNodeDelete(tree, root->right, min->data);
}
}else {
treeNode tmp = root;
root = root->left ? root->left : root->right;
free(tmp);
tree->size--;
}
}
calHeight(root);
}
return root;
}
void avlTreeDelete(avlTree tree, int val) {
tree->root = avlTreeNodeDelete(tree, tree->root, val);
tree->height = getHeight(tree->root);
}
int main() {
avlTree tree = NULL;
avlTreeInit(&tree);
int nums[] = {3, 2, 1, 4, 5, 6, 7, 16, 15, 14, 13, 12, 11, 10, 8, 9};
int len = sizeof(nums) / sizeof(int);
for (int i = 0; i < len; i++) {
avlTreeInsert(tree, nums[i]);
printf("树的高度%d,树的size:%d\n", tree->height, tree->size);
printTree(tree);
printTreeByLevel(tree);
printf("\n");
}
printf("-------开始删除---------------------------------\n\n");
for (int i = 0; i < len; i++) {
avlTreeDelete(tree, nums[i]);
printf("---------删除%d----树的高度:%d,树的size:%d---\n", nums[i], tree->height, tree->size);
printTreeByLevel(tree);
printf("--------------------------------------------\n\n");
}
printf("-------删除结束---------------------------------\n\n");
return 0;
}
function AvlTree() {
this.root = null;
this.size = 0;
this.height = 0;
}
function Node(element) {
this.element = element;
this.height = 1;
this.left = null;
this.right = null;
}
function getHeight(node) {
if (node === null) {
return 0;
}
return node.height;
}
function calHeight(node) {
if (node === null) {
return;
}
node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
}
function calBf(node) {
return getHeight(node.left) - getHeight(node.right);
}
AvlTree.prototype._rotateLL = function(node) {
var l = node.left;
var lr = l.right;
l.right = node;
node.left = lr;
calHeight(node);
calHeight(l);
return l;
};
AvlTree.prototype._rotateRR = function(node) {
var r = node.right;
var rl = r.left;
r.left = node;
node.right = rl;
calHeight(node);
calHeight(r);
return r;
};
AvlTree.prototype._rotateLR = function(node) {
node.left = this._rotateRR(node.left);
return this._rotateLL(node);
};
AvlTree.prototype._rotateRL = function(node) {
node.right = this._rotateLL(node.right);
return this._rotateRR(node);
};
AvlTree.prototype.insert = function(element) {
this.root = this._insert(element, this.root);
this.height = getHeight(this.root);
};
AvlTree.prototype._insert = function(element, node) {
if (node === null) {
this.size++;
return new Node(element, parent);
}
if (node.element > element) {
node.left = this._insert(element, node.left);
if (calBf(node) == 2) {
if (node.left.element > element) {
node = this._rotateLL(node);
} else {
node = this._rotateLR(node);
}
}
} else if (node.element < element) {
node.right = this._insert(element, node.right);
if (calBf(node) == -2) {
if (node.right.element < element) {
node = this._rotateRR(node);
} else {
node = this._rotateRL(node);
}
}
}
calHeight(node);
return node;
};
AvlTree.prototype.delete = function(element) {
if (this.root !== null) {
this.root = this._delete(element, this.root);
}
};
AvlTree.prototype._delete = function(element, node) {
if (node != null) {
if (node.element > element) {
node.left = this._delete(element, node.left);
if (calBf(node) == -2) {
if (height(node.right.left) > height(node.right.right)) {
node = this._rotateRL(node);
} else {
node = this._rotateRR(node);
}
}
} else if (node.element < element) {
node.right = this._delete(element, node.right);
if (calBf(node) == 2) {
if (getHeight(node.left.left) > getHeight(node.left.right)) {
node = this._rotateLL(node);
} else {
node = this._rotateLR(node);
}
}
} else {
if (node.left != null && node.right != null) {
if (getHeight(node.left) > getHeight(node.right)) {
max = this.maxNode(node.left);
node.element = max.element;
node.left = this._delete(max.element, node.left);
} else {
min = this.minNode(node.right);
node.element = min.element;
node.right = this._delete(min.element, node.right);
}
} else {
node = node.left != null ? node.left : node.right;
this.size--;
}
}
}
return node;
};
AvlTree.prototype.minNode = function(node) {
if (node.left === null) {
return node;
}
return this.minNode(node.left);
};
AvlTree.prototype.maxNode = function(node) {
if (node.right === null) {
return node;
}
return this.maxNode(node.right);
};
AvlTree.prototype.forEach = function(func) {
this._forEach(this.root, func);
};
AvlTree.prototype._forEach = function(node, func) {
if (node !== null) {
this._forEach(node.left, func);
func(node);
this._forEach(node.right, func);
}
};