zoukankan      html  css  js  c++  java
  • 算法-搜索(3)AVL树

    AVL树高度平衡的二叉搜索树,任一点的平衡印章只能是+1、-1、0,从而尽量降低树的高度。

    如果它有n个结点,高度可保持在O(log2n),平均搜索长度也可保持在O(log2n)。

    (1)AVL树的插入

    在插入一个新结点时,需要从插入位置沿通向根的路径回溯,检查各结点左右子树高度差。

    发现结点的平衡因子为0,刚刚是在其较矮的子树插入新结点,从该结点到根的路径上各结点为根的子树高度不变、平衡因子不变,无需继续检查。

    发现结点平衡因子|bf|=1,说明插入前是0,插入后该结点没有失去平衡。但该子树高度增加了,还需要继续往根方向检查。

    发现某一结点|bf|=2不平衡,停止回溯,做平衡化旋转。

    从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。

    1.这三个结点位于同一条直线上,采用单旋转进行平衡化

    bf=2,右子树高,查看右结点bf=1,执行左单旋转

    执行后ptr和subL的bf均变为0:

    clip_image002

    bf=-2,左子树高,查看左结点bf=-1,执行右单旋转

    执行后ptr和subL的bf均变为0

    clip_image003

    2.这三个结点位于一条折线上,采用双旋转进行平衡化

    bf=2,右子树高,查看右结点bf=-1,无论ptr->bf是1还是-1,都执行先右后左双旋转

    如果ptr的bf原为1,右单旋转后subR的bf改为0,先右后左双旋转,旋转后subL的bf变为-1

    如果ptr的bf原为-1,右单旋转后subR的bf改为1,先右后左双旋转,旋转后subL的bf变为0

    ptr的bf最终变为0

    clip_image004

    bf=-2,左子树高,查看左结点bf=1,无论ptr->bf是1还是-1,都执行先左后右双旋转

    如果ptr的bf原为-1,左单旋转后subL的bf改为0,先左后右双旋转,旋转后subR的bf变为1

    如果ptr的bf原为1,左单旋转后subL的bf改为-1,先左后右双旋转,旋转后subR的bf变为0

    ptr的bf最终变为0

    clip_image005

    (2)AVL树的删除

    1.被删结点有两个子女

    寻找被删结点p在中序下的直接前驱或者直接后继q,把q的值给结点p,然后把结点q删除(q最多一个子女)

    clip_image006

    2.被删结点最多一个子女q

    直接把p的父结点pr中原来指向p的指针指向q。

    q是pr的左子女的话,pr的bf加1;q是pr的右子女的话,pr的bf减1。

    (1)pr的bf原为0,左/右子树被缩短后改为1/-1,以pr为根的子树高度未变,pr到根结点的路径上所有结点都不需要调整。

    clip_image007

    (2)pr的bf原不为0,较高的子树被缩短,bf变为0。

    以pr为根的子树虽然平衡,但高度减少了。需要继续考察其父结点。

    clip_image008

    (3)pr的bf原不为0,较矮的子树又被缩短,bf变为±2。令q指向pr的较高的子树

    ①q->bf==0的情况:

    pr的bf=2,右子树较高,右子树q的bf为0,作左单旋转

    旋转后q->bf=pr->bf=0,还要把q->pf改为-1,pr->bf改为1

    clip_image009

    pr的bf=-2,左子树较高,左子树q的bf为0,作右单旋转

    旋转后q->bf=pr->bf=0,还要把q->pf改为1,pr->bf改为-1

    ②pr->bf和q->bf正负号相同的情况:

    pr的bf=2,右子树较高,右子树q的bf=1,执行左单旋转,旋转后q->bf=pr->bf=0

    clip_image010

    pr的bf=-2,左子树较高,左子树q的bf=-1,执行右单旋转,旋转后q->bf=pr->bf=0

    ③pr和p的bf正负号相反,执行双旋转,先绕q转一次,再绕pr转一次:

    clip_image011

    pr的bf=2,右子树较高,右子树q的bf为-1,执行先右后左双旋转

    pr的bf=-2,左子树较高,左子树q的bf为1,执行先左后右双旋转

    处理后子树高度减少1,继续考察其父结点。

    #include <iostream>
    #include "stack.h"
    using namespace std;
    
    template <class E,class K>
    struct AVLNode:public BSTNode<E,K>{
        int bf;   //右子树高度-左子树高度
        AVLNode():left(NULL),right(NULL),bf(0){}
        AVLNode(E d,AVLNode<E,K> *l=NULL,AVLNode<E,K> *r=NULL):data(d),left(l),right(r),bf(0){}
    }
    
    template <class E,class K>
    class AVLTree:public BST<E,K>{
    public:
        AVLTree():root(NULL){}
        AVLTree(K Ref):RefValue(Ref),root(NULL){}
        bool Insert(E& el){return Insert(root,el);}
        bool Remove(K x,E& el){return Remove(root,el);}
        friend istream& operator>>(istream& in,AVLTree<E,K>& tree);
        friend ostream& operator<<(ostream& out,AVLTree<E,K>& tree);
        int Height()const;
    protected:
        AVLNode<E,K> *Search(K x,AVLNode<E,K>* &par)const;
        bool Insert(AVLNode<E,K>* &ptr,E& el);
        bool Remove(AVLNode<E,K>* &ptr,K x,E& el);
        void RotateL(AVLNode<E,K>* &ptr);
        void RotateR(AVLNode<E,K>* &ptr);
        void RotateLR(AVLNode<E,K>* &ptr);
        void RotateRL(AVLNode<E,K>* &ptr);
        int Height(AVLNode<E,K> *ptr)const;
        void AVLTree<E,K>::Traverse(AVLNode<E,K> *ptr,ostream& out)const;
    }
    
    template <class E,class K>
    void AVLTree<E,K>::RotateL(AVLNode<E,K>* &ptr){
        //右子树比左子树高,对以ptr为根的AVL树做左单旋转,旋转后新根在ptr
        AVLNode<E,K> *subL=ptr;
        ptr=subL->right;  //两个结点平衡因子均为正,需要做左单旋转
        subL->right=ptr->left;
        ptr->left=subL;
        ptr->bf=subL->bf=0;
    }
    
    template <class E,class K>
    void AVLTree<E,K>::RotateR(AVLNode<E,K>* &ptr){
        //左子树比右子树高,对以ptr为根的AVL树做右单旋转,旋转后新根在ptr
        AVLNode<E,K> *subR=ptr;
        ptr=subR->left;    //两个结点平衡因子均为负,需要做右单旋转
        subR->left=ptr->right;
        ptr->right=subR;
        ptr->bf=subR->bf=0;
    }
    
    template <class E,class K>
    void AVLTree<E,K>::RotateLR(AVLNode<E,K>* &ptr){
        AVLNode<E,K> *subR=ptr,*subL=ptr->left;
        ptr=subL->right;
        subL->right=ptr->left;
        ptr->left=subL;
        if(ptr->bf<=0) subL->bf=0;  //如果ptr->bf原为-1,左单旋转后subL->bf为0
        else subL->bf=-1;           //如果ptr->bf原为1,左单旋转后subL->bf为-1
        subR->left=ptr->right;
        ptr->right=subR;
        if(ptr->bf==-1) subR->bf=1; //如果ptr->bf原为-1,先左后双旋转后subR->bf为1
        else subR->bf=0;            //如果ptr->bf原为1,先左后双旋转后subR->bf为0
        ptr->bf=0;
    }
    
    template <class E,class K>
    void AVLTree<E,K>::RotateRL(AVLNode<E,K>* &ptr){
        AVLNode<E,K> *subL=ptr,*subR=ptr->right;
        ptr=subR->left;
        subR->left=ptr->right;
        ptr->right=subR;
        if(ptr->bf>=0) subR->bf=0;
        else subR->bf=1;
        subL->right=ptr->left;
        ptr->left=subL;
        if(ptr->bf==1) subL->bf=-1;
        else subL->bf=0;
        ptr->bf=0;
    }
    
    template <class E,class K>
    bool AVLTree<E,K>::Insert(AVLNode<E,K>* &ptr,E& el){   //改变bf的步骤在旋转中做了,Insert方法中无需再改变
        AVLNode<E,K> *pr=NULL,*p=ptr,*q;
        int d;
        stack<AVLNode<E,K>*> st;
        while(p!=NULL){
            if(el==p->data) return false;
            pr=p;
            st.push(pr);
            if(el<p->data) p=p->left;
            else p=p->right;
        }
        p=new AVLNode<E,K>(el);
        if(p==NULL){
            cerr<<"存储空间不足!"<<endl;
            exit(1);
        }
        if(pr==NULL){    //空树,新结点成为根结点
            ptr=p;
            return true;
        }
        if(el<pr->data) pr->left=p;
        else pr->right=p;
        while(!st.IsEmpty()){
            st.Pop(pr);
            if(p==pr->left) pr->bf--;  //调整父结点平衡因子
            else pr->bf++;
            //第一种情况,平衡因子为0,说明刚才在pr较矮的子树上插入新结点,结点pr处平衡且高度未变。结点pr到根的路径上所有结点为根的子树高度都未变。可以直接结束重新平衡化的处理。
            if(pr->bf==0) break;
            //第二种情况,平衡因子绝对值为1,说明插入前平衡因子为0,插入后以pr为根的子树没有失去平衡,但仍然需要检查结点pr到根的路径上所有结点为根的子树
            if(pr->bf==1 || pr->bf==-1) p=pr;
            //第三种情况,平衡因子绝对值为2,说明新结点在较高的子树上插入,造成了不平衡,需要做平衡化旋转
            else{
                d=(pr->bf<0)?-1:1;
                if(p->bf==d){        //两结点平衡因子同号,单旋转
                    if(d==-1) RotateR(pr);    //pr的bf为-2,p的bf为1,右单旋转
                    else RotateL(pr);         //pr的bf为2,p的bf为-1,左单旋转
                }
                else{
                    if(d==-1) RotateLR(pr);   //pr的bf为2,p的bf为-1,先右后左旋转
                    else RotateRL(pr);        //pr的bf为-2,p的bf为-1,先左后右旋转
                }
                break;  //旋转之后以pr为根的子树高度降低,无需再向上层回溯
            }
        }
        if(st.IsEmpty()) ptr=pr;  //栈空,pr作为新根
        else{
            st.getTop(q);        //栈不空,取出pr的父结点
            if(q->data>pr->data) q->left=pr;
            else q->right=pr;
        }
        return true;
    }
    
    template <class E,class K>
    istream& operator>>(istream& in,AVLTree<E,K>& Tree){
        E item;
        cout<<"Construct AVL tree:
    ";
        cout<<"Input Data(end with"<<Tree.RefValue<<"):";
        in>>item;
        while(item.key!=Tree.RefValue){
            Tree.Insert(item);
            cout<<"Input Data(end with"<<Tree.RefValue<<"):";
            in>>item;
        }
        return in;
    }
    
    template <class E,class K>
    ostream& operator<<(ostream& out,const AVLTree<E,K>& Tree){
        out<<"Inorder traversal of AVL tree.
    ";
        Tree.Traverse(Tree.root,out);
        out<<endl;
        return out;
    }
    
    template <class E,class K>
    void AVLTree<E,K>::Traverse(AVLNode<E,K> *ptr,ostream& out)const{
        if(ptr!=NULL){
            Traverse(ptr->left,out);
            out<<ptr->data<<'';
            Traverse(ptr->right,out);
        }
    }
    
    template <class E,class K>
    bool AVLTree<E,K>::Remove(AVLNode<E,K>* &ptr,K x,E& el){
        AVLNode<E,K> *pr=NULL,*p=ptr,*q,*ppr;
        int d,dd=0;
        while(p!=NULL){
            if(k==p->data.key) break;
            pr=p;
            st.Push(pr);
            if(k<p->data.key) p=p->left;
            else p=p->right;
        }
        if(p==NULL) return false;
        if(p->left!=NULL && p->right!=NULL){  //被删结点有两个子女
            pr=p;
            st.Push(pr);
            q=p->left;  //在p的左子树找p的直接前驱
            while(q->right!=NULL){
                pr=q;
                st.Push(pr);
                q=q->right;
            }
            p->data=q->data;
            p=q;  //被删结点转换为q,q最多一个子女
        }
        if(p->left!=NULL) q=p->left;  //被删结点只有一个子女或者没有子女
        else q=p->right;
        if(pr==NULL) ptr=q;  //被删结点为根结点,让其子女成为新根
        else{    //被删结点不是根结点
            if(pr->left==p) pr->left=q;   //p的父结点pr原本指向p的指针改指向q
            else pr->right=q;
            while(!st.IsEmpty()){      //重新平衡化
                st.Pop(pr);            //从栈中推出父结点
                if(pr->right==q) pr->bf--;  //调整父结点的平衡因子
                else pr->bf++;
                if(!st.IsEmpty()){
                    st.getTop(ppr);     //从栈中取出祖父结点
                    dd=(ppr->left==pr)?-1:1;  //旋转后与上层链接方向
                }
                else dd=0;                   //栈空,旋转后不与上层链接
                if(pr->bf==1 || pr->bf==-1)//pr的平衡因子原为0,在它的左/右子树被缩短后,平衡因子变为1或者-1,以pr为根的子树高度没有改变,从pr到根结点的路径上所有结点都不需要调整
                    break;
                if(pr->bf!=0){
                    if(pr->bf<0){d=-1;q=pr->left;}
                    else{d=1;q=pr->right;}
                    if(q->bf==0){
                        if(d==-1){  //pr的平衡因子为-2,左子树q的平衡因子为0,执行右单旋转
                            RotateR(pr);
                            pr->bf=1;
                            pr->left->bf=-1;
                        }
                        else{      //pr的平衡因子为2,右子树q的平衡因子为0,执行左单旋转
                            RotateL(pr);
                            pr->bf=-1;
                            pr->right->bf=1;
                        }
                        break;
                    }
                    if(q->bf==d){  //q和pr的平衡因子正负号相同,执行一个单旋转来恢复平衡
                        if(d==-1) RotateR(pr);  //pr平衡因子为-2,p平衡因子为-1,右单旋转
                        else RotateL(pr);  //pr平衡因子为2,p平衡因子为1,左单旋转
                    }
                    else{          //q和pr的平衡因子正负号相反,执行一个双旋转来恢复平衡
                        if(d==-1) RotateLR(pr);   //pr的bf=-2,左子树较高,左子树p的bf为1,执行先左后右双旋转
                        else RotateRL(pr);        //pr的bf=2,右子树较高,右子树p的bf为-1,执行先右后左双旋转
                    }
                    if(dd==-1) ppr->left=pr;
                    else if(dd==1) ppr->right=pr;    //旋转后新根与上层链接
                }
                q=pr;    //pr的平衡因子变为0(较高的子树被缩短了),此时以pr为根的树平衡但高度减少1,需要继续考察pr父结点的平衡状态
            }
            if(st.IsEmpty()) ptr=pr;  //调整到树的根结点
        }
        delete p;
        return true;
    }
  • 相关阅读:
    IE678下,select 诡异的样式
    跟着我一步一步的搭建一个基于springcloud的微服务实例
    关于Future踩过的坑
    Apache下的SocketClient的使用
    Jaxb处理泛型,转化成xml字符串
    Linux Centos虚拟机扩容
    docker 搭建zookeeper集群和kafka集群
    sysbench 数据库性能测试工具的使用
    docker 容器技术
    自己手写实现Dubbo
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/9237545.html
Copyright © 2011-2022 走看看