AVL平衡树 - 二叉搜索树的扩展1

来源:互联网 发布:国外it技术博客 编辑:程序博客网 时间:2024/05/17 22:32

    • AVL树的介绍
    • 实现
      • 1 旋转
        • LL左左
        • RR右右
        • LR左右
        • RL右左
      • 2 插入
      • 3 删除
    • 性能
    • 完整代码和参考资料

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

0 0