您的位置:首页 > 理论基础 > 数据结构算法

AVL树插入、删除的分析与实现

2014-07-10 10:19 369 查看

AVL树基本概念

一棵AVL(Adelson-Velskii and Landis)树满足下面三个条件:

是一棵二叉搜索树(Binary Search Tree);
对于树中的每一个结点,其左、右子树的高度差的绝对值小于等于1;
空树的高度定义为-1。
如图1,左边的是一棵AVL树,而右边的则不是(17是不平衡的,其左子树的高度为1,而右子树的高度为-1),但它们都是二叉搜索树。



[align=center]图1 AVL树和二叉搜索树

AVL树在基本的二叉搜索树上加以限制条件来控制树的平衡,施加的条件可以多样化,比如可以要求每个结点的的左右子树高度差的绝对值为0,但这太严格了,实际上只有满二叉树能达到这个要求;若再把限制条件放松一点点,即允许高度差的绝对值不超过1,那么就成了AVL树;如果再把限制条件放松,还可以定义出其它类型的平衡树。

[/align]

AVL树的插入操作

插入操作同普通的二叉搜索树的插入操作时一样的,可以参考数据结构教材,这儿不详述。对于AVL树,插入一个新结点后,可能导致上述条件2被破坏。导致这种情况发生的情形有4种,下面只分析其中两种,另外两种情况是对称的。我们假设首先被打破平衡状态的结点为α。

情形1——插入到α的左孩子的左子树中

如图2左边所示,此时可以将α进行右旋转,得到右边的结果,旋转后的根节点变成了β,整棵树维持平衡。



[align=center]图2 左-左插入及平衡恢复

[/align]

情形2——插入到α的左孩子的右子树中

如图3左边所示,注意:此时的B和C高度不可能同时为h-1。对于这种情形,需要进行两次旋转,先对β进行左旋转,然后对α进行右旋转,整棵树维持平衡。



[align=center]图3 左-右插入及平衡恢复[/align]

情形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的话,那么β是不平衡的,这与我们假设的第一个被打破平衡的结点为α是矛盾的。此时只需对α进行右旋转就可以了。



[align=center]图4 删除结点的情形1[/align]

情形2——α的左孩子的左子树高度为h

这种情形如图5所示,被删除的结点原来位于D中,删除后D的高度由h+1变成了h,导致α失去平衡。与上面的情形1不同,这次我们假设A的高度为h,那么上面图4中的B高度必为h+1,据此,我们画出图5。按照我们的假设,图5中的B和C的高度不可能同时为h-1(想想为什么?)。此时,先对β进行左旋转,再对α进行右旋转。



[align=center]图 5 删除结点的情形2[/align]

类别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 numb
c948
ers 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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息