AVL树基本概念
一棵AVL(
Adelson-
Velskii and
Landis)树满足下面三个条件:
- 是一棵二叉搜索树(Binary Search Tree);
- 对于树中的每一个结点,其左、右子树的高度差的绝对值小于等于1;
- 空树的高度定义为-1。
如图1,左边的是一棵AVL树,而右边的则不是(17是不平衡的,其左子树的高度为1,而右子树的高度为-1),但它们都是二叉搜索树。
图1 AVL树和二叉搜索树
AVL树在基本的二叉搜索树上加以限制条件来控制树的平衡,施加的条件可以多样化,比如可以要求每个结点的的左右子树高度差的绝对值为0,但这太严格了,实际上只有满二叉树能达到这个要求;若再把限制条件放松一点点,即允许高度差的绝对值不超过1,那么就成了AVL树;如果再把限制条件放松,还可以定义出其它类型的平衡树。
AVL树的插入操作
插入操作同普通的二叉搜索树的插入操作时一样的,可以参考数据结构教材,这儿不详述。对于AVL树,插入一个新结点后,可能导致上述条件2被破坏。导致这种情况发生的情形有4种,下面只分析其中两种,另外两种情况是对称的。我们假设首先被打破平衡状态的结点为α。
情形1——插入到α的左孩子的左子树中
如图2左边所示,此时可以将α进行右旋转,得到右边的结果,旋转后的根节点变成了β,整棵树维持平衡。
图2 左-左插入及平衡恢复
情形2——插入到α的左孩子的右子树中
如图3左边所示,
注意:此时的B和C高度不可能同时为h-1。对于这种情形,需要进行两次旋转,先对β进行左旋转,然后对α进行右旋转,整棵树维持平衡。
图3 左-右插入及平衡恢复
情形3——插入到α的右孩子的右子树中
这与情形1是对称的。
情形4——插入到α的右孩子的左子树中
这与情形2是对称的。
插入操作C语言伪代码
设函数avl_insert()接受参数为一个AvlTree类型的变量以及要插入的键值key,函数返回插入key后的新的AVL树,则伪代码如下:
AvlTree avl_insert(AvlTree t, int key)
{
if (NULL == t) {
/* alloc new node here and assign it to t */
} else if (t->key > key) {
t->left = avl_insert(t->left, key);
if (t is unbalanced) {
if (key < t->left->key)
t = single_rotate_r(t); /* Left-Left */
else
t = double_rotate_lr(t); /* Left-Right */
}
} else if (t->key < key) {
t->right = avl_insert(t->right, key);
if (t is unbalanced) {
if (key > t->right->key)
t = single_rotate_l(t); /* Right-Right */
else
t = double_rotate_rl(t); /* Right-Left */
}
} else
; /* duplicate key, do nothing */
/* adjust t's height */
t->height = max(height(t->left), height(t->right)) + 1;
return t;
}
AVL树的删除操作
删除结点后,我们设第一个被打破平衡状态的结点为α。相应的,删除操作也有4种情况,但其实可以分为两大类:
- 删除的结点属于α的左子树;
- 删除的结点属于α的右子树。
我们将分析第一类中的两种情形,另外两种情形是类似的。
类别1——删除α的右子树中的某个结点
假设结点被删除后,α的右子树高度为h。
情形1——α的左孩子的左子树高度为h+1
这种情形如图4所示,被删除的结点原来位于D中,删除后D的高度由h+1变成了h,导致α失去平衡。我们假设A的高度为h+1,而不用理会B的高度,但可以肯定的是B的高度只能是h或h+1,否则如果B的高度为h-1的话,那么β是不平衡的,这与我们假设的第一个被打破平衡的结点为α是矛盾的。此时只需对α进行右旋转就可以了。
图4 删除结点的情形1
情形2——α的左孩子的左子树高度为h
这种情形如图5所示,被删除的结点原来位于D中,删除后D的高度由h+1变成了h,导致α失去平衡。
与上面的情形1不同,这次我们假设A的高度为h,那么上面图4中的B高度必为h+1,据此,我们画出图5。按照我们的假设,图5中的B和C的高度不可能同时为h-1(想想为什么?)。此时,先对β进行左旋转,再对α进行右旋转。
图 5 删除结点的情形2
类别2——删除α的左子树中的某个结点
情形1——α的右孩子的右子树高度为h+1
这与类别1中的情形1是对称的。
情形2——α的右孩子的右子树高度为h
这与类别1中的情形2是对称的。
删除操作C语言伪代码
和插入操作类似,这儿不列出它的伪代码,文章末尾会给出完整的AVL插入、删除操作的C语言实现。作为练习,读者可以先思考一下如何实现删除操作,如果你理解了上面讲述的情形1和情形2,那么就不难得出答案,否则你可能还没有完全理解删除操作,可以再多思考思考。
AVL树的完整C语言实现
/*
* Copyright © 2014 YU Heng-yang. All rights reserved.
*
* avl_tree.c - Simple implementation of AVL tree.
*
* Modify key to other types as you like, note also
* the prototype of exported functions and key
* compare function.
*
* TODO:
* Implements avl_remove() operation, more complicate
* than expected.
*
* 2014-7-7 YU Heng-yang.
*
* Rev1:
* Implementated avl_remove() operation.
*
* 2014-7-9 YU Hengy-yang.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#define ALLOC(size) malloc(size)
#define FREE(p) free(p)
typedef struct AvlTree *AvlTree;
struct AvlTree {
int key;
int height;
AvlTree left, right;
};
/* exported functions */
AvlTree avl_new(void);
AvlTree avl_insert(AvlTree t, int key);
AvlTree avl_remove(AvlTree t, int key);
AvlTree avl_find(AvlTree t, int key);
AvlTree avl_destroy(AvlTree t);
void avl_traversal(AvlTree t, void (*apply) (AvlTree t, void *arg), void *arg);
/* internal functions */
static AvlTree single_rotate_l(AvlTree t);
static AvlTree single_rotate_r(AvlTree t);
static AvlTree double_rotate_lr(AvlTree t);
static AvlTree double_rotate_rl(AvlTree t);
static AvlTree rebalance(AvlTree t);
static AvlTree avl_find_max(AvlTree t);
static int height(AvlTree t);
static int max(int x, int y);
static void check(AvlTree t, int d);
static void print_tree(AvlTree t);
static void print(AvlTree t, void *arg);
/* test driver */
static void handcraft_test(int n);
static void autorun_test(int n);
int main(int argc, char *argv[])
{
int n;
/*
* Sample input:
* step1: 13
* step2: 13 4 20 -7 10 15 30 -18 1 8 11 27 7
* step3: enter 13 integers randomly
* step4: 20 13 -7 7 30 1 11 27 10 4 -18 15 8
*/
puts("How many numbers you will input?(For example, 5 or enter 0 to skip this step)");
scanf("%d", &n);
handcraft_test(n);
/*
* Sample input:
*
* 1000
*/
puts("How many numbers do you want for autorun?(For example, 5000)");
scanf("%d", &n);
autorun_test(n);
return 0;
}
AvlTree avl_new(void)
{
return NULL;
}
/*
* Draw some pictures to get an insight.
* Hint:
* There are four cases that need rotation(s)
* to rebalance the tree. Tow of them are symmetric to
* the other two. Height of empty trees is -1.
* Besides calling rebalance(), the code commented
* out is another way to rebalance t.
*
*/
AvlTree avl_insert(AvlTree t, int key)
{
if (NULL == t) {
AvlTree newt = ALLOC(sizeof(*newt));
newt->key = key;
newt->left = newt->right = NULL;
newt->height = 0;
t = newt;
} else if (t->key > key) {
t->left = avl_insert(t->left, key);
/* if (height(t->left) - height(t->right) >= 2) { */
/* if (key < t->left->key) */
/* t = single_rotate_r(t); /\* Left-Left *\/ */
/* else */
/* t = double_rotate_lr(t); /\* Left-Right *\/ */
/* } */
t = rebalance(t);
} else if (t->key < key) {
t->right = avl_insert(t->right, key);
/* if (height(t->right) - height(t->left) >= 2) { */
/* if (key > t->right->key) */
/* t = single_rotate_l(t); /\* Right-Right *\/ */
/* else */
/* t = double_rotate_rl(t); /\* Right-Left *\/ */
/* } */
t = rebalance(t);
} else
; /* duplicate key, do nothing */
t->height = max(height(t->left), height(t->right)) + 1;
return t;
}
/*
* Again, draw some pictures to get an insight.
* Hint:
* There four cases that need rotation(s) to
* rebalance the tree. Two of them are symmetric to
* the other two.
*/
AvlTree avl_remove(AvlTree t, int key)
{
if (t) {
if (t->key > key) {
t->left = avl_remove(t->left, key);
t = rebalance(t);
} else if (t->key < key) {
t->right = avl_remove(t->right, key);
t = rebalance(t);
} else {
if (t->left && t->right) {
AvlTree lmax = avl_find_max(t->left);
t->key = lmax->key;
t->left = avl_remove(t->left, lmax->key);
/* t may be unbalanced after remove key from t->left */
t = rebalance(t);
} else {
/* at most one child of t is not empty */
AvlTree ret;
if (!t->left)
ret = t->right;
else
ret = t->left;
FREE(t);
t = ret;
}
}
}
if (t)
t->height = max(height(t->left), height(t->right)) + 1;
return t;
}
AvlTree avl_find(AvlTree t, int key)
{
if (t) {
if (t->key > key)
return avl_find(t->left, key);
else if (t->key < key)
return avl_find(t->right, key);
else
return t;
}
return NULL;
}
AvlTree avl_destroy(AvlTree t)
{
if (t) {
t->left = avl_destroy(t->left);
t->right = avl_destroy(t->right);
FREE(t);
}
return NULL;
}
void avl_traversal(AvlTree t, void (*apply) (AvlTree t, void *arg), void *arg)
{
if (t) {
assert(apply);
avl_traversal(t->left, apply, arg);
apply(t, arg);
avl_traversal(t->right, apply, arg);
}
}
/* Precondition: t has right child */
static AvlTree single_rotate_l(AvlTree t)
{
AvlTree rchild;
assert(t->right);
rchild = t->right;
t->right = rchild->left;
rchild->left = t;
t->height = max(height(t->left), height(t->right)) + 1;
rchild->height = max(height(rchild->left), height(rchild->right)) + 1;
return rchild;
}
/* Precondition: t has left child */
static AvlTree single_rotate_r(AvlTree t)
{
AvlTree lchild;
assert(t->left);
lchild = t->left;
t->left = lchild->right;
lchild->right = t;
t->height = max(height(t->left), height(t->right)) + 1;
lchild->height = max(height(lchild->left), height(lchild->right)) + 1;
return lchild;
}
/* Precondition: t has left child which has right child */
static AvlTree double_rotate_lr(AvlTree t)
{
assert(t->left && t->left->right);
t->left = single_rotate_l(t->left);
return single_rotate_r(t);
}
/* Precondition: t has right child which has left child */
static AvlTree double_rotate_rl(AvlTree t)
{
assert(t->right && t->right->left);
t->right = single_rotate_r(t->right);
return single_rotate_l(t);
}
static AvlTree avl_find_max(AvlTree t)
{
if (t)
while (t->right)
t = t->right;
return t;
}
static AvlTree rebalance(AvlTree t)
{
if (t) {
int diff = height(t->left) - height(t->right);
assert(diff >= -2 && diff <= 2);
/* check(t, 2); */
if (diff == -2) {
diff = height(t->right->right) - height(t->left);
if (diff > 0) {
assert(diff == 1);
t = single_rotate_l(t);
} else {
assert(diff == 0);
t = double_rotate_rl(t);
}
}
if (diff == 2) {
diff = height(t->left->left) - height(t->right);
if (diff > 0) {
assert(diff == 1);
t = single_rotate_r(t);
} else {
assert(diff == 0);
t = double_rotate_lr(t);
}
}
}
return t;
}
static int height(AvlTree t)
{
if (NULL == t)
return -1;
return t->height;
}
static int max(int x, int y)
{
return x > y ? x : y;
}
static void print_tree(AvlTree t)
{
if (t) {
print_tree(t->left);
printf("(%d, %d) ", t->key, height(t));
print_tree(t->right);
}
}
static void check(AvlTree t, int d)
{
if (t) {
int diff;
check(t->left, d);
check(t->right, d);
diff = height(t->left) - height(t->right);
/* dump out tree */
if (diff < -d || diff > d) {
printf("Left height = %d, Right height = %d\n",
height(t->left), height(t->right));
print_tree(t);
}
assert(diff >= -d && diff <= d);
}
}
static void print(AvlTree t, void *arg)
{
assert(arg);
printf((char *)arg, t->key);
}
void handcraft_test(int ninput)
{
int i, n;
AvlTree tree = avl_new();
/* insert */
for (i = 0; i < ninput; i++) {
scanf("%d", &n);
tree = avl_insert(tree, n);
check(tree, 1);
printf("After insert %d: ", n);
avl_traversal(tree, print, "%d ");
/* in practice, height() will not be exported */
printf(", Height: %d", height(tree));
putchar('\n');
}
/* find */
for (i = 0; i < ninput; i++) {
scanf("%d", &n);
printf("%s\n", avl_find(tree, n) ? "Found" : "Not Found");
}
/* remove */
for (i = 0; i < ninput; i++) {
scanf("%d", &n);
tree = avl_remove(tree, n);
check(tree, 1);
printf("After remove %d: ", n);
avl_traversal(tree, print, "%d ");
/* in practice, height() will not be exported */
printf(", Height: %d", height(tree));
putchar('\n');
}
tree = avl_destroy(tree);
assert(NULL == tree);
assert(NULL == avl_find(tree, 100));
assert(NULL == avl_remove(tree, -100));
avl_traversal(tree, print, "%d ");
}
void autorun_test(int n)
{
if (n > 0) {
int i, *keys;
AvlTree tree = avl_new();
keys = ALLOC(n * sizeof(*keys));
/* generate random numbers */
srand(time(NULL));
for (i = 0; i < n; i++)
keys[i] = rand();
/* insert one by one */
for (i = 0; i < n; i++) {
tree = avl_insert(tree, keys[i]);
/* printf("Insert %d, Height %d\n", keys[i], height(tree)); */
}
check(tree, 1);
printf("AVL tree generated, height is %d\n", height(tree));
/* print_tree(tree); */
/* putchar('\n'); */
/* try to find some of them */
for (i = 0; i < n; i++) {
/* make some not found cases*/
int key = i & 1 ? rand() : keys[i];
avl_find(tree, key);
/* printf("%d:%s\n", key, avl_find(tree, key) ? "Found":"Not found"); */
}
puts("AVL find operation finished.");
/* remove one by one */
for (i = 0; i < n; i++) {
/* equal chance for remove or not remove */
/* int key = i & 1 ? rand() : keys[i]; */
int key = keys[i];
/* check(tree, 1); */
/* printf("\nBefore remove %d: ", key); */
/* print_tree(tree); */
/* printf("Remove %d\n", key); */
tree = avl_remove(tree, key);
/* printf("\nAfter remove %d: ", key); */
/* print_tree(tree); */
/* printf(", Height %d\n", height(tree)); */
}
puts("AVL remove operation finished");
tree = avl_destroy(tree);
FREE(keys);
}
}