AVL平衡树的理解(插入、查找、删除)

AVL平衡树的理解(插入、查找、删除)

  • Dargon

  • 2020/11/21

  • 所遇到的的重要的问题:

  • 教科书 来自:《数据结构和算法分析》第四章 树

AVL平衡树出现的原因

  • 在一般的二叉树的插入和删除操作中,想想出现最坏的情况,
  1. 例如以20-26的顺讯进行插入的话,就类似形成一个线性链表的结构,让树的深度变得很深,不易搜索
  2. 进行删除操作的时候,一般过程,找到需要删除的节点,然后,找到该节点左子树的最大值,将其值保存下来,进行删除,把值更新到原本需要删除的节点,这样完成一次删除操作。久而久之,进行大量的删除、插入操作会出现树的不平衡往左侧。

关于节点的旋转

左单旋转

结合图进行观看,很直观
在这里插入图片描述

static AvlNode *single_rotate_with_left(AvlNode *k2) {
    AvlNode *k1;
    k1 =k2->left;
    k2->left =k1->right;
    k1->right =k2;

    k2->height =( Max( height_avltree(k2->left), height_avltree(k2->right) ) ) +1;
    k1->height =( Max( height_avltree(k1->left), k2->height ) ) +1;

    return k1; //return the new root node
}

右单旋转

基本和左单旋转是对称的
在这里插入图片描述

static AvlNode *single_rotate_with_right(AvlNode *k2) {
    AvlNode *k1;
    k1 =k2->right;
    k2->right =k1->left;
    k1->left =k2;

    k2->height =( Max( height_avltree(k2->left), height_avltree(k2->right) ) ) +1;
    k1->height =( Max( height_avltree(k1->right), k2->height ) ) +1;

    return k1; //return the new root node
}

左双旋转

实际分析是进行两次旋转,先进行右旋转,之后左旋转
在这里插入图片描述

static AvlNode *double_rotate_with_left(AvlNode *k3) {
    k3->left =single_rotate_with_right(k3->left);
    return single_rotate_with_left(k3);
}

右双旋转

先进行左旋转,之后进行右旋转

static AvlNode *double_rotate_with_right(AvlNode *k3) {
    k3->right =single_rotate_with_left(k3->right);
    return single_rotate_with_right(k3);
}

树的插入

直接看代码,正常插入,遇见不平衡(一个节点的左右子树高度差为 2,就是不平衡的)进行旋转调整
考虑四种情况:

  1. 左边的左边 形成/ 形状
  2. 左边的右边 形成< 形状
  3. 右边的左边 形成> 形状
  4. 右边的右边 形成\ 形状

1 和 4 对应的一次单旋转即可
2 和 3 对应的是双旋转

AvlNode *insert_avlnode(TREE_TYPE value, AvlNode *tree) {
    if(tree ==NULL) {
        tree =(AvlNode *)malloc(sizeof(AvlNode));
        assert( tree != NULL );
        tree->value =value;
        tree->height =0;
        tree->left =tree->right =NULL;
    }
    else if(value <tree->value) { //在左边插入
        tree->left =insert_avlnode(value, tree->left);
        //判断当前节点,左右子树的高度差,一旦该值为2,就是不平衡了,需要旋转调整
        if( ((height_avltree(tree->left)) -(height_avltree(tree->right))) ==2 ) {
            if( value <tree->left->value ) { //这里形成 “/” 形状 需要单左旋转
                tree =single_rotate_with_left(tree);
            }
            else { //这里形成 “<” 形状 需要左-右双旋转
                tree =double_rotate_with_left(tree);
            }
        }       
    }
    else if(value >tree->value) { //在右边插入
        tree->right =insert_avlnode(value, tree->right);
        if( ((height_avltree(tree->right)) -(height_avltree(tree->left))) ==2 ) {
            if( value >tree->right->value ) { //这里形成 “\” 形状 需要单右旋转
                tree =single_rotate_with_right(tree);
            }
            else { //这里形成 “>” 形状 需要右-左双旋转
                tree =double_rotate_with_right(tree);
            }
        }
    }
    else {
        //value already exist .So do nothing !
    }
    tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
    return tree;
}

树的查找

运用遍历(traverse)很方便的查找

AvlNode *find_avlnode( AvlNode *tree, TREE_TYPE value ) {
    if(tree ==NULL) {
        printf("Tree is NULL\n");
        return NULL;
    }
    if(value <tree->value) {
        return find_avlnode(tree->left, value);
    }
    else if(value >tree->value) {
        return find_avlnode(tree->right, value);
    }
    else {
        return tree;
    }
}

树的删除

关于删除右边元素的情况,两种复杂情况的画图分析(左边和其类似)
在这里插入图片描述

AvlNode *delete_avlnode( AvlNode *tree, TREE_TYPE value ) {
    AvlNode *temp =NULL;
    AvlNode **link;

    link =&avltree;
    if(tree ==NULL) {
        printf("节点不存在\n");
        return NULL;
    }
    if( value >tree->value ) { //目标值大,去树的右边寻找
        tree->right =delete_avlnode( tree->right, value ); //删除之后 进行调整
        //判断当前节点,左右子树的高度差,一旦该值为2,就是不平衡了,需要旋转调整
        tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
        if( height_avltree(tree->left) -height_avltree(tree->right) ==2 ) {
            if( height_avltree(tree->left->left) >=height_avltree(tree->left->right) ) {  
                //删除右边节点,导致左边高度差大,这里形成 “/” 形状 需要单右旋转
                tree =single_rotate_with_left(tree);
            }
            else { //这里形成 “<” 形状 需要右-左双旋转
                tree =double_rotate_with_left(tree);
            }
        }
    }
    else if( value <tree->value ) { //目标值小,去树的左边寻找
        tree->left =delete_avlnode( tree->left, value );
        tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
        if( (height_avltree(tree->right) -height_avltree(tree->left)) ==2 ) {
            if( height_avltree(tree->right->left) <=height_avltree(tree->right->right) ) { //这里形成 “\” 形状 需要单左旋转
                tree =single_rotate_with_right(tree);
            }
            else { //这里形成 “>” 形状 需要左-右双旋转 
                tree =double_rotate_with_right(tree);
            }
        }            
    }
    else if( value ==tree->value ) { //利用递归 最终找到目标值(删除节点)
        temp =tree;
        if( tree->left ==NULL && tree->right ==NULL ) { //删除节点为叶节点
            tree =NULL;
            free(temp);
        }
        else if( tree->left !=NULL && tree->right ==NULL ) { //删除节点只有左边有子树
            tree =tree->left;
            free(temp);
        }
        else if( tree->left ==NULL && tree->right !=NULL ) { //删除节点只有右边有子树
            tree =tree->right;
            free(temp);
        }
        else { //删除节点两边都有子树
            temp =find_min_avlnode(tree->right);
            tree->value =temp->value;
            tree->right =delete_avlnode(tree->right, tree->value);
        }
    }
    return tree;
}

总结

  • 在学习两天的AVL 树的理解,这些比较用脑子,保持着思考就行,再此记录一下,以后方便知识的回顾,遇到事情,多思考!有些事情来了,就接着,愁眉苦脸垂头丧气永远解决不了问题的!加油!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值