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

AVL树基本概念

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

  1. 是一棵二叉搜索树(Binary Search Tree);
  2. 对于树中的每一个结点,其左、右子树的高度差的绝对值小于等于1;
  3. 空树的高度定义为-1。

如图1,左边的是一棵AVL树,而右边的则不是(17是不平衡的,其左子树的高度为1,而右子树的高度为-1),但它们都是二叉搜索树。

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

图1 AVL树和二叉搜索树

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

AVL树的插入操作

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

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

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

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

图2 左-左插入及平衡恢复

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

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

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

图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. 删除的结点属于α的左子树;
  2. 删除的结点属于α的右子树。

我们将分析第一类中的两种情形,另外两种情形是类似的。

类别1——删除α的右子树中的某个结点

假设结点被删除后,α的右子树高度为h。

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

这种情形如图4所示,被删除的结点原来位于D中,删除后D的高度由h+1变成了h,导致α失去平衡。我们假设A的高度为h+1,而不用理会B的高度,但可以肯定的是B的高度只能是h或h+1,否则如果B的高度为h-1的话,那么β是不平衡的,这与我们假设的第一个被打破平衡的结点为α是矛盾的。此时只需对α进行右旋转就可以了。
《AVL树插入、删除的分析与实现》
图4 删除结点的情形1

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

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

    原文作者:AVL树
    原文地址: https://blog.csdn.net/you_dian_tian/article/details/37610153
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞