算法-搜索(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;
}
原文地址:https://www.cnblogs.com/yangyuliufeng/p/9237545.html