zoukankan      html  css  js  c++  java
  • 数据结构AVL树

    平衡二叉树相信大家都不陌生了,它是一颗平衡的二叉排序树,它的查询、插入删除的时间复杂度均为O(logn),是非常好且重要的动态查找结构,因此被广泛的应用。
    而平衡二叉树的实现因为引入了旋转操作而经常让人难以理解,楼主写这篇文章的目的,其实是希望能够通过造轮子的方式,帮助到大家完全理解从普通二叉排序树逐步优化到平衡二叉树(AVL树)的过程。

    一、平衡二叉树之AVL树简介
    AVL树是一种高度平衡的平衡二叉树,树中的任意节点的左右子树的高度之差不超过1。
    如果将二叉树上结点的平衡因子BF(Balanced Factor)定义为该结点的左子树与右子树的高度之差,根据AVL树的定义,AVL树中的任意结点的平衡因子只可能是-1(右子树高于左子树)、0或者1(左子树高于右子树),在某些图中也会表示为绝对高度差,即0,1,2这种形式,请注意理解。
    它通过树的旋转操作来维护平衡特性。
    下面我会逐步通过C++代码来理解、最终实现AVL树。

    二、AVL树实现

    1.二叉排序树实现

    AVL树是一颗平衡的二叉排序树,所以为了实现AVL树,我们可以先从实现一颗普通的二叉排序树入手,实现一颗普通二叉排序树。为了接下来将二叉排序树优化为AVL树,代码中将树直接取名为AVL。

    树中节点具有双亲和左右两个子节点的指针,

    插入删除操作都是先查找到节点位置,再进行插入或者删除。

    删除时,如果删除的是同时有左右子树的非叶子节点,则寻找该节点的后继节点,替换该节点再将后继节点删除,这里不做过多解释,代码如下:

    #include<stdio.h>
    #include<malloc.h>
    
    struct AVLNode{
        int value;
        int balance;  //AVL树的平衡字段,普通二叉排序树并不需要用到该字段
        AVLNode* left;
        AVLNode* right;
        AVLNode* parent;
    };
    //这是一颗普通的二叉排序树,,命名为AVLTree仅仅因为方便起见
    struct AVLTree{
        AVLNode* root;
    }tree;
    //查询操作
    struct AVLNode* select(int value,AVLNode* root){
        if(root==0){
            return 0;
        }
        if(root->value==value){
            return root;
        }
        if(root->value>value){
            if(root->left)
                return select(value,root->left);
            else
                return root;
        }
        if(root->value<value){
            if(root->right)
                return select(value,root->right);
            else
                return root;
        }
    }
    //插入操作
    void insert(int value,AVLNode* root){
        AVLNode* node=select(value,root);
        if(node==0){
            tree.root=(AVLNode*)malloc(sizeof(AVLNode));
            tree.root->value=value;
            tree.root->left=tree.root->right=0;
            tree.root->parent=0;
            tree.root->balance=0;
        }
        else if(node->value!=value){
            if(node->value>value){
                node->left=(AVLNode*)malloc(sizeof(AVLNode));
                node->left->value=value;
                node->left->left=node->left->right=0;
                node->left->parent=node;
                node->left->balance=0;
            }else if(node->value<value){
                node->right=(AVLNode*)malloc(sizeof(AVLNode));
                node->right->value=value;
                node->right->left=node->right->right=0;
                node->right->parent=node;
                node->right->balance=0;
            }
        }
    }
    void delnodeifhas1childornot(AVLNode* a){
        if(a->parent==0){
            if(a->left){
                tree.root=a->left;
                a->left->parent=0;
            }else{
                tree.root=a->right;
                a->right->parent=0;
            }
        }else{
            if(a->parent->left==a){
                if(a->left){
                    a->parent->left=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->left=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }else{
                if(a->left){
                    a->parent->right=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->right=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }
        }
    }
    struct AVLNode* getmin(AVLNode* a){
        if(a->left)
            getmin(a->left);
        else
            return a;
    } 
    void delnodeifhas2child(AVLNode* a){
        AVLNode* after=getmin(a->right);
        a->value=after->value;
        delnodeifhas1childornot(after);
    }
    //删除操作
    void del(int value,AVLNode* root){
        AVLNode* node=select(value,root);
        if(node->value==value){
            if(node->left&&node->right){
                delnodeifhas2child(node);
            }else{
                delnodeifhas1childornot(node);
            }
        }
    }

    2.从二叉排序树到AVL树

    2.1 旋转操作原理

    AVL树通过左旋右旋来实现它的平衡性。理解左旋和右旋是AVL树实现的关键点,那么问题来了,所谓左旋右旋又具体是怎么样的操作呢?以右旋为例。

    初始的AVL树有两个节点,a和b,此时树平衡。

                  

    插入节点c,若节点c小于b,则成为b的左子节点,此时a左子树深度为2,右子树深度为0,处于不平衡状态,此时执行右旋操作。

                               

    右旋就是使a、b进行右旋1/4圆,使得b节点作为父节点,a节点变为b节点的右节点,b的左子树在经过右旋操作后仍是b的左子树。

    如果b有右子树。那么右旋后b的右子树将成为a的左子树。

    这就是右旋操作的具体实现,左旋是右旋对称的过程。当二叉树插入或者删除一个节点时,如果破坏了树的平衡性,就会根据树的失衡情况进行旋转操作,如果左子树高于右子树,则右旋,如果右子树高于左子树,则左旋。

    右旋及左旋的代码如下:

    
    struct AVLNode* turnRight(AVLNode* a){
        // 右旋使a的父节点的子节点替换为b
        AVLNode* b=a->left;
        if(a->parent!=0){
            if(a->parent->right==a){
                a->parent->right=b;
            }else{
                a->parent->left=b;
            }
        }
        b->parent=a->parent;
        //将b的右子树作为a的左子树,并将a作为b的右子树
        a->parent=b;
        a->left=b->right;
        if(a->left!=0)
            a->left->parent=a;
        b->right=a;
        //重新设置a、b节点的balance值
        setBalance(a);
        setBalance(b);
        //返回b节点
        return b;
    }    
    
    
    struct AVLNode* turnLeft(AVLNode* a){
        //左旋把a的父节点的子节点替换为b
        AVLNode* b=a->right;
        if(a->parent!=0){
            if(a->parent->right==a){
                a->parent->right=b;
            }else{
                a->parent->left=b;
            }
        }
        b->parent=a->parent;
        //将a作为b的左子树,并将b的左子树作为a的右子树
        a->parent=b;
        a->right=b->left;
        b->left=a;
        if(a->right!=0)
            a->right->parent=a;
        //重新设置a\b的balance值
        setBalance(a);
        setBalance(b);
        //返回b节点
        return b;
    }
    int height(AVLNode* a){
        if(a==0){
            return 0;
        }
        int rightheight=height(a->right);
        int leftheight=height(a->left);
        return rightheight > leftheight ? (rightheight+1) : (leftheight+1);
    }
    void setBalance(AVLNode* a){
        if(a)
            a->balance=height(a->right)-height(a->left);
    }

    上面将树从失衡状态恢复到平衡状态只进行了一次旋转操作,事实上还有一种稍微复杂的情况,需要进行两次旋转才能将树重新恢复平衡

    如果节点插入到a的左子树的右节点,则需要先左旋再右旋来重新平衡树。同理,如果节点插入到a的右子树的左子节点,则需要先右旋再左旋来重新平衡树。如果直接进行右旋,生成的树将仍然是失衡的。

    代码如下,调用了上面实现的旋转操作:

    struct AVLNode* turnLeftThenRight(AVLNode* a){
        a->left = turnLeft(a->left);
        return turnRight(a);
    }
    struct AVLNode* turnRightThenLeft(AVLNode* a){
        a->right = turnRight(a->right);
        return turnLeft(a);
    }

    2.2 实现AVL树插入删除

     AVL树的insert操作,仅仅在二叉排序树的insert基础上增加了rebalance操作。

    //AVL树在insert或者del之后进行rebalance操作,从插入节点的父节点开始,逐层向上遍历进行rebalance,直到遍历到根节点,由于树是平衡的,所以rebalance的时间复杂度是O(logn)。
    void rebalance(AVLNode* a){
        setBalance(a);
        if(a->balance== -2){                   //如果左子树比右子树高2层,需要通过旋转来重新平衡
            if(a->left->balance <=0){          //如果左子节点的左子树比右子树高,则进行一次右旋;如果左子节点的右子树比左子树高,则先进行左旋,再进行右旋。
                a=turnRight(a);
            }else{
                a=turnLeftThenRight(a);
            }
        }else if(a->balance==2){       //如果右子树比左子树高2层,需要通过旋转来重新平衡
            if(a->right->balance>=0){       //如果右子节点的右子树比左子树高,则进行一次左旋;如果右子节点的右子树比左子树低,则先进行右旋,再进行左旋。
                a=turnLeft(a);
            }else{
                a=turnRightThenLeft(a);
            }
        }
        if(a->parent){
            rebalance(a->parent);
        }else{
            tree.root=a;
        }
    }
    
    void insert(int value,AVLNode* root){
        AVLNode* node=select(value,root);
        if(node==0){
            tree.root=(AVLNode*)malloc(sizeof(AVLNode));
            tree.root->value=value;
            tree.root->left=tree.root->right=0;
            tree.root->parent=0;
            tree.root->balance=0;
        }
        else if(node->value!=value){
            if(node->value>value){
                node->left=(AVLNode*)malloc(sizeof(AVLNode));
                node->left->value=value;
                node->left->left=node->left->right=0;
                node->left->parent=node;
                node->left->balance=0;
                rebalance(node);           //插入完成后进行rebalance
            }else if(node->value<value){
                node->right=(AVLNode*)malloc(sizeof(AVLNode));
                node->right->value=value;
                node->right->left=node->right->right=0;
                node->right->parent=node;
                node->right->balance=0;
                rebalance(node);           //插入完成后进行rebalance
            }
        }
    }

    AVL树的删除操作,也只是在二叉排序树的删除操作上,增加了rebalance操作。

    void delnodeifhas1childornot(AVLNode* a){
        if(a->parent==0){
            if(a->left){
                tree.root=a->left;
                a->left->parent=0;
            }else{
                tree.root=a->right;
                a->right->parent=0;
            }
        }else{
            if(a->parent->left==a){
                if(a->left){
                    a->parent->left=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->left=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }else{
                if(a->left){
                    a->parent->right=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->right=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }
            rebalance(a->parent);
        }
    }
    struct AVLNode* getmin(AVLNode* a){
        if(a->left)
            getmin(a->left);
        else
            return a;
    } 
    void delnodeifhas2child(AVLNode* a){
        AVLNode* after=getmin(a->right);
        a->value=after->value;
        delnodeifhas1childornot(after);
    }
    void del(int value,AVLNode* root){         //删除操作
        AVLNode* node=select(value,root);
        if(node->value==value){
            if(node->left&&node->right){
                delnodeifhas2child(node);
            }else{
                delnodeifhas1childornot(node);
            }
        }
    }

    完整AVL树c++代码如下:

    #include<stdio.h>
    #include<malloc.h>
    
    struct AVLNode{
        int value;
        int balance;
        AVLNode* left;
        AVLNode* right;
        AVLNode* parent;
    };
    
    struct AVLTree{
        AVLNode* root;
    }tree;
    
    int height(AVLNode* a){
        if(a==0){
            return 0;
        }
        int rightheight=height(a->right);
        int leftheight=height(a->left);
        return rightheight > leftheight ? (rightheight+1) : (leftheight+1);
    }
    struct AVLNode* select(int value,AVLNode* root){
        if(root==0){
            return 0;
        }
        if(root->value==value){
            return root;
        }
        if(root->value>value){
            if(root->left)
                return select(value,root->left);
            else
                return root;
        }
        if(root->value<value){
            if(root->right)
                return select(value,root->right);
            else
                return root;
        }
    }
    void setBalance(AVLNode* a){
        if(a)
            a->balance=height(a->right)-height(a->left);
    }
    
    struct AVLNode* turnLeft(AVLNode* a){
        AVLNode* b=a->right;
        if(a->parent!=0){
            if(a->parent->right==a){
                a->parent->right=b;
            }else{
                a->parent->left=b;
            }
        }
        b->parent=a->parent;
        a->parent=b;
        a->right=b->left;
        b->left=a;
        if(a->right!=0)
            a->right->parent=a;
        setBalance(a);
        setBalance(b);
        return b;
    }
    struct AVLNode* turnRight(AVLNode* a){
        AVLNode* b=a->left;
        if(a->parent!=0){
            if(a->parent->right==a){
                a->parent->right=b;
            }else{
                a->parent->left=b;
            }
        }
        b->parent=a->parent;
        a->parent=b;
        a->left=b->right;
        if(a->left!=0)
            a->left->parent=a;
        b->right=a;
        setBalance(a);
        setBalance(b);
        return b;
    }
    
    struct AVLNode* turnLeftThenRight(AVLNode* a){
        a->left = turnLeft(a->left);
        return turnRight(a);
    }
    struct AVLNode* turnRightThenLeft(AVLNode* a){
        a->right = turnRight(a->right);
        return turnLeft(a);
    }
    void rebalance(AVLNode* a){
        setBalance(a);
        if(a->balance== -2){
            if(a->left->balance <=0){
                a=turnRight(a);
            }else{
                a=turnLeftThenRight(a);
            }
        }else if(a->balance==2){
            if(a->right->balance>=0){
                a=turnLeft(a);
            }else{
                a=turnRightThenLeft(a);
            }
        }
        if(a->parent){
            rebalance(a->parent);
        }else{
            tree.root=a;
        }
    }
    
    void insert(int value,AVLNode* root){
        AVLNode* node=select(value,root);
        if(node==0){
            tree.root=(AVLNode*)malloc(sizeof(AVLNode));
            tree.root->value=value;
            tree.root->left=tree.root->right=0;
            tree.root->parent=0;
            tree.root->balance=0;
        }
        else if(node->value!=value){
            if(node->value>value){
                node->left=(AVLNode*)malloc(sizeof(AVLNode));
                node->left->value=value;
                node->left->left=node->left->right=0;
                node->left->parent=node;
                node->left->balance=0;
                rebalance(node);
            }else if(node->value<value){
                node->right=(AVLNode*)malloc(sizeof(AVLNode));
                node->right->value=value;
                node->right->left=node->right->right=0;
                node->right->parent=node;
                node->right->balance=0;
                rebalance(node);
            }
        }
    }
    void delnodeifhas1childornot(AVLNode* a){
        if(a->parent==0){
            if(a->left){
                tree.root=a->left;
                a->left->parent=0;
            }else{
                tree.root=a->right;
                a->right->parent=0;
            }
        }else{
            if(a->parent->left==a){
                if(a->left){
                    a->parent->left=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->left=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }else{
                if(a->left){
                    a->parent->right=a->left;
                    a->left->parent=a->parent;
                }else{
                    a->parent->right=a->right;
                    if(a->right)
                        a->right->parent=a->parent;
                }
            }
            rebalance(a->parent);
        }
    }
    struct AVLNode* getmin(AVLNode* a){
        if(a->left)
            getmin(a->left);
        else
            return a;
    } 
    void delnodeifhas2child(AVLNode* a){
        AVLNode* after=getmin(a->right);
        a->value=after->value;
        delnodeifhas1childornot(after);
    }
    void del(int value,AVLNode* root){
        AVLNode* node=select(value,root);
        if(node->value==value){
            if(node->left&&node->right){
                delnodeifhas2child(node);
            }else{
                delnodeifhas1childornot(node);
            }
        }
    }
    void insertNodetest(){
        insert(5,tree.root);
        insert(6,tree.root);
        insert(7,tree.root);
        insert(3,tree.root);
        insert(4,tree.root);
        insert(4,tree.root);
        insert(5,tree.root);
        insert(5,tree.root);
        insert(3,tree.root);
        insert(8,tree.root);
        insert(9,tree.root);
        insert(10,tree.root);
        insert(11,tree.root);
        insert(12,tree.root);
    }
    void delNodetest(){
        del(6,tree.root);
        del(11,tree.root);
        del(12,tree.root);
        del(3,tree.root);
        del(3,tree.root);
        del(5,tree.root);
        del(9,tree.root);
        del(12,tree.root);
    }
    int main(){
        insertNodetest(); // 测试构建AVL树
        delNodetest();    // 测试删除AVL树节点
    }

    至此,我们就完全实现了一颗AVL树的插入删除和查询功能。

  • 相关阅读:
    springboot笔记(五章)整合持久层技术
    Java基础复习(一)
    springboot复习
    JUC(2)
    JUC(1)
    springboot笔记(一~四章)入门、基础配置、整合视图技术、整合Web开发
    JVM复习
    NIO(New IO)
    hive 函数
    hive基础知识
  • 原文地址:https://www.cnblogs.com/grj001/p/12223761.html
Copyright © 2011-2022 走看看