码迷,mamicode.com
首页 > 其他好文 > 详细

AVL平衡树 - 二叉搜索树的推广

时间:2015-04-28 23:00:58      阅读:330      评论:0      收藏:0      [点我收藏+]

标签:avl

1. AVL树的介绍

AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的。
它是最先发明的自平衡二叉查找树,也被称为高度平衡树。相比于”二叉查找树”,它的特点是:AVL树中任何节点的两个子树的高度最大差别为1
技术分享 技术分享
如上图(左):非AVL树,如对节点9,左子树高度为0,右子树高度为2
如上图(由):AVL树,对任意节点的两个子树高度最大差1

2. 实现

当因为插入或删除导致AVL的平衡被破坏时,需要旋转处理来修复。
如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)
对于左右的情况为什么不是直接以3节点为轴向左旋转? 因为旋转只能处理根节点和它的左节点(或右节点),来把它的左节点(右节点)提升到根。换句话说,旋转的输入参数(轴)必须是根,提升的只能是字节点,下降的只能是根。
(多级旋转,必须是从底层开始,到顶层结束)
技术分享

2.1 旋转

LL左左

因为插入或删除,使得根节点的左节点的左节点非空,导致根的左子树比右子树高度大2;只需以根为输入,一次右旋,将原根的左节点旋转到根

//LL情况时,一次右旋--k2是根,k1是k2的左子树,将k1旋转成根 -- 以k2为原点向右旋
AVLTree rotLL(AVLTree k2) 
{
    AVLTree k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;
    k2->height = MAX(AVLTreeHeight(k2->left), AVLTreeHeight(k2->right)) + 1;
    k1->height = MAX(AVLTreeHeight(k1->left), k2->height) + 1;
    return k1;
}

RR右右

因为插入或删除,使得根节点的右节点的右节点非空,导致根的右子树比左子树高度大2;只需以根为输入,一次左旋,将原根的右节点旋转到根

//RR情况时,一次左旋---k2是根,k1是k2的右子树,(k1的右子树非空)将k1旋转成根 -- 以k2为原点向左旋
AVLTree rotRR(AVLTree k2) 
{
    AVLTree k1 = k2->right;
    k2->right = k1->left;
    k1->left = k2;
    k2->height = MAX(AVLTreeHeight(k2->left), AVLTreeHeight(k2->right)) + 1;
    k1->height = MAX(k2->height, AVLTreeHeight(k1->right)) + 1;
    return k1;
}

LR左右

插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致”根的右子树的高度”比”根的左子树的高度”大2,导致AVL树失去了平衡。需要2次旋转,先以根的右节点为输入做一次左旋(即RR的情况),此时树的形状为LL,再做一次右旋(即LL)。

//LR情况时,先左旋在右旋:k3是根,k1是k3的左子树,k2是k1的右子树,
// 那么先以k1为原点左旋,再以k3为原点右旋,最终把k2旋转到根(相当于先RR旋转,再LL旋转)
AVLTree rotLR(AVLTree k3) 
{
    k3->left = rotRR(k3->left); //从底层开始到顶层结束
    return rotLL(k3);
}

AVLTree rotRL(AVLTree k3) 
{
    k3->right = rotLL(k3->right); //从底层开始到顶层结束
    return rotRR(k3);
}

RL右左

类似于LR情况,不做赘述

2.2 插入

首先,AVL的插入和搜索二叉树的插入相同。不同的是插入会影响AVL的平衡,当因为插入导致不平衡时(左右子树高度差2),同样存在上述的四种情况,需要对应的旋转处理。

AVLTree AVLTreeInsert(AVLTree tree, Item key)
{   
    if (tree == NULL)
        return NewNode(key, NULL, NULL);
    if (key < tree->key) {
        //插入到左子树
        tree->left = AVLTreeInsert(tree->left, key);
        //由于插入到左子树上的节点导致不平衡
        if (Height(tree->left) - Height(tree->right) > 1) {
            //插入到左子树的左侧。则是LL,反之则是LR
            if (key < tree->left->key) 
                tree = rotLL(tree);
            else
                tree = rotLR(tree);
        } 
    } else if (key > tree->key) {
        tree->right = AVLTreeInsert(tree->right, key);
        if (Height(tree->right) - Height(tree->left) > 1) {
            if (key > tree->right->key)
                tree = rotRR(tree);
            else
                tree = rotRL(tree);
        }
    } else {
        printf("\nExsited key = %d, Insert Failed\n", key);
        // return tree;
    }

    tree->height = MAX(Height(tree->left), Height(tree->right)) + 1;
    return tree;
}

2.3 删除

删除同样可能影响AVL的平衡。
- 比如说,如果因为删除左子树中的节点,导致右子树比左子树高度大2,那么此时再观察右子树的左右子树的高度,若右子树的右子树比左子树高,那么则是RR的情况,反之则是RL的情况。
- 而针对于匹配到待删除节点 : 如果恰好是根节点:那么删除根后,左右子树高度还是最大差1,不需旋转;如果不是根(即待删除的在左右子树中),那么在上述两种情况已经判断了是否旋转。
- 对于删除节点后的连接:如果左右子树都非空
若左子树高,则找出左子树的最大值替换根的值,然后在左子树中删除该最大值的节点;
若右子树高,则找出右子树的最小值替换根的值,然后在右子树中删除该最小值的节点;
这样保证了AVL以更大的可能维持平衡。

AVLTree AVLTreeDelete(AVLTree tree, Item key)
{
    if (tree == NULL)
        return NULL;
    //待删除节点在左子树中
    if (key < tree->key) { 
        tree->left = AVLTreeDelete(tree->left, key);
        //左子树中删除,若右子树高度比左大2(失去平衡),将右子树中的某节点旋转到根,降低高度
        if (Height(tree->right) - Height(tree->left) > 1) {
            if (Height(tree->right->right) > Height(tree->right->left))
                tree = rotRR(tree);
            else
                tree = rotRL(tree);
        }

    } else if (key > tree->key) {
        tree->right = AVLTreeDelete(tree->right, key);
        //右子树中删除,左子树高度比右大2
        if (Height(tree->left) - Height(tree->right) > 1) {
            if (Height(tree->left->left) > Height(tree->left->right))
                tree = rotLL(tree);
            else
                tree = rotLR(tree);

        }
    }  else {//匹配到待删除
        //待删除节点两字树非空
        if (tree->left && tree->right) {
            if (Height(tree->left) > Height(tree->right)) {
                //左高,找到左子树中的最大值,替换根的值
                AVLTree avl_max = findAVLMax(tree->left);
                tree->key = avl_max->key;
                AVLTreeDelete(tree->left, avl_max->key);
            } else {
                //右高,找到右子树中的最小值,替换根的值
                AVLTree avl_min = findAVLMin(tree->right);
                tree->key = avl_min->key;
                AVLTreeDelete(tree->right, avl_min->key);
            }
        } else {
            //有一个为空,或者2个为空
            AVLTree tmp = tree;
            tree = (tree->left == NULL) ? (tree->right) : (tree->left);
            free(tmp);
            return tree;
        }
    }   
    return tree;
}

3. 性能

  1. 向AVL树插入,可以透过如同它是未平衡的二叉查找树一样,把给定的值插入树中,接着自底往上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.44乘log n个节点,即O(log n),而每次AVL旋转都耗费固定的时间,所以插入处理在整体上的耗费为O(logn)时间。
  2. 从AVL树中删除,可以透过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。本文没有采用
  3. 搜索,可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。(这是与伸展树搜寻相对立的,它会因为搜寻而变更树结构。)

4. 完整代码和参考资料

#include <stdio.h>
#include <stdlib.h>
#define MAX(A, B) ((A > B) ? A : B)
#define Height(tree) ((tree == NULL) ? 0 : (tree->height))
typedef int Item;
typedef struct AVLTreeNode AVLTreeNode;
typedef AVLTreeNode* AVLTree;
struct AVLTreeNode {
    Item key;
    AVLTree left;
    AVLTree right;
    int height;
};
static int g_error = 0; //错误代码
AVLTree NewNode(Item key, AVLTree left, AVLTree right)
{
    AVLTree x = (AVLTree)malloc(sizeof(*x));
    if (x == NULL) {
        g_error = 1;
        exit(-1);
    }
    x->key = key;
    x->left = left;
    x->right = right;
    x->height = MAX(Height(x->left), Height(x->right)) + 1;
    return x;
}

AVLTree AVLTreeInit()
{
    return NewNode(10, NULL, NULL);    
}

int AVLTreeHeight(AVLTree tree)
{
    return (tree == NULL) ? 0 : (tree->height);
}

//RR情况时,一次左旋---k2是根,k1是k2的右子树,(k1的右子树非空)将k1旋转成根 -- 以k2为原点向左旋
AVLTree rotRR(AVLTree k2) 
{
    AVLTree k1 = k2->right;
    k2->right = k1->left;
    k1->left = k2;
    k2->height = MAX(AVLTreeHeight(k2->left), AVLTreeHeight(k2->right)) + 1;
    k1->height = MAX(k2->height, AVLTreeHeight(k1->right)) + 1;
    return k1;
}

//LL情况时,一次右旋--k2是根,k1是k2的左子树,将k1旋转成根 -- 以k2为原点向右旋
AVLTree rotLL(AVLTree k2) 
{
    AVLTree k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;
    k2->height = MAX(AVLTreeHeight(k2->left), AVLTreeHeight(k2->right)) + 1;
    k1->height = MAX(AVLTreeHeight(k1->left), k2->height) + 1;
    return k1;
}

//LR情况时,先左旋在右旋:k3是根,k1是k3的左子树,k2是k1的右子树,
// 那么先以k1为原点左旋,再以k3为原点右旋,最终把k2旋转到根(相当于先RR旋转,再LL旋转)
AVLTree rotLR(AVLTree k3) 
{
    k3->left = rotRR(k3->left); //从底层开始到顶层结束
    return rotLL(k3);
}

AVLTree rotRL(AVLTree k3) 
{
    k3->right = rotLL(k3->right); //从底层开始到顶层结束
    return rotRR(k3);
}

AVLTree AVLTreeInsert(AVLTree tree, Item key)
{   
    if (tree == NULL)
        return NewNode(key, NULL, NULL);
    if (key < tree->key) {
        tree->left = AVLTreeInsert(tree->left, key);
        if (Height(tree->left) - Height(tree->right) > 1) {
            if (key < tree->left->key)
                tree = rotLL(tree);
            else
                tree = rotLR(tree);
        } 
    } else if (key > tree->key) {
        tree->right = AVLTreeInsert(tree->right, key);
        if (Height(tree->right) - Height(tree->left) > 1) {
            if (key > tree->right->key)
                tree = rotRR(tree);
            else
                tree = rotRL(tree);
        }
    } else {
        printf("\nExsited key = %d, Insert Failed\n", key);
        // return tree;
    }

    tree->height = MAX(Height(tree->left), Height(tree->right)) + 1;
    return tree;
}

void traversal(AVLTree tree)
{
    if (tree == NULL) {
        printf("NIL\t");
        return;
    }
    printf("%d\t", tree->key);
    traversal(tree->left);
    traversal(tree->right);
    return;
}
AVLTree findAVLMin(AVLTree tree)
{
    if(tree == NULL || tree->left == NULL)
        return tree;
    return findAVLMin(tree->left);
}
AVLTree findAVLMax(AVLTree tree)
{
    if (tree == NULL || tree->right == NULL)
        return tree;
    return findAVLMax(tree->right);
}

AVLTree AVLTreeDelete(AVLTree tree, Item key)
{
    if (tree == NULL)
        return NULL;
    //待删除节点在左子树中
    if (key < tree->key) { 
        tree->left = AVLTreeDelete(tree->left, key);
        //左子树中删除,右子树高度比左大2,将右子树中的某节点旋转到根,降低高度
        if (Height(tree->right) - Height(tree->left) > 1) {
            if (Height(tree->right->right) > Height(tree->right->left))
                tree = rotRR(tree);
            else
                tree = rotRL(tree);
        }

    } else if (key > tree->key) {
        tree->right = AVLTreeDelete(tree->right, key);
        //右子树中删除,左子树高度比右大2
        if (Height(tree->left) - Height(tree->right) > 1) {
            if (Height(tree->left->left) > Height(tree->left->right))
                tree = rotLL(tree);
            else
                tree = rotLR(tree);

        }
    }  else {       //匹配到待删除节点 : 如果恰好是根节点:那么删除跟后,左右子树高度还是最大差1,不需旋转;如果不是根,那么在上述两种情况判断是否旋转
        //待删除节点两字树非空
        if (tree->left && tree->right) {
            if (Height(tree->left) > Height(tree->right)) {
                //左高,找到左子树中的最大值,替换根的值
                AVLTree avl_max = findAVLMax(tree->left);
                tree->key = avl_max->key;
                AVLTreeDelete(tree->left, avl_max->key);
            } else {
                //右高,找到右子树中的最小值,替换根的值
                AVLTree avl_min = findAVLMin(tree->right);
                tree->key = avl_min->key;
                AVLTreeDelete(tree->right, avl_min->key);
            }
        } else {
            AVLTree tmp = tree;
            tree = (tree->left == NULL) ? (tree->right) : (tree->left);
            free(tmp);
            return tree;
        }
    }   
    return tree;
}

int main()
{
    AVLTree avl_tree = NULL;
    for (int i = 0; i < 10; i++) {
        int key = rand()%100;
        avl_tree = AVLTreeInsert(avl_tree, key);
        printf("%d\t", key);
    }
    printf("\nTraversal\n");
    traversal(avl_tree);

    AVLTreeDelete(avl_tree, 41);
    printf("\nDeleted Traversal\n");
    traversal(avl_tree);
    getchar();
}

参考资料:
1. AVL树(一)之 图文解析 和 C语言的实现:http://www.cnblogs.com/skywang12345/p/3576969.html
2. AVL树-维基百科:https://zh.wikipedia.org/wiki/AVL%E6%A0%91

AVL平衡树 - 二叉搜索树的推广

标签:avl

原文地址:http://blog.csdn.net/quzhongxin/article/details/45341349

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!