AVL平衡二叉树的实现

什么是AVL树?AVL也是一颗搜索二叉树,右子不小于父值,左子不大于父值。平衡的要求是每一个节点的平衡因子在-1,0,1中取,平衡因子=右树高度-左树高度。

C语言实现的代码:

include <iostream>
using namespace std;
class AvlNode{
public:
    int key;
    int bf;/*balance factor 平衡因子*/
    AvlNode * left, * right;
    AvlNode(int k,int b=0,AvlNode * l=NULL,AvlNode * r=NULL){
        key = k;
        bf = b;
        left = l;
        right = r;
    }
};
//一个链式栈
class StackNode{
public:
    AvlNode * data;
    StackNode * next;
    StackNode(AvlNode * data,StackNode * next = NULL){
        this->data = data;
        this->next = next;
    }
    StackNode():data(NULL),next(NULL){}
};
class AvlStack{
public:
    AvlStack();
    ~AvlStack();
    void Push(AvlNode * & n);
    AvlNode * Pop();
    void output(){
        StackNode * p = top;
        while(p){
            cout<<p->data->key<<" ";
            p = p->next;
        }
        cout<<endl;
    }
    bool IsEmpty(){
        return top==NULL ? true : false;
    }
    AvlNode * GetTop(){
        return top->data;
    }
private:
    StackNode * top;
};
AvlStack::AvlStack():top(NULL){
/*    AvlNode * t = new AvlNode(5);
    Push(t);       
    Push(new AvlNode(2));       
    Push(new AvlNode(0));       
    Push(new AvlNode(9));       
    Push(new AvlNode(4));
    cout<<(Pop() ? Pop()->key : -1)<<" ";
    output();*/
}
AvlStack::~AvlStack(){
    //释放内存
    StackNode * p = top;
    while(p){
        top = p->next;
        delete p;
        p = top;
    }
}
void AvlStack::Push(AvlNode * & n){
    StackNode * t = new StackNode(n,top);
    top = t;
}
AvlNode * AvlStack::Pop(){
    if(NULL == top)
        return NULL;
    StackNode * p = top;
    top = top->next;
    AvlNode * n = p->data;
    delete p;
    return n;
}
//结束
/**
 * 首先AVL树也是一棵搜索树,它也满足左子不大于父,右子不小于父
 * 而且 节点的左右子树高度不超过1 空树高度为零、只有根为一
 * bf 指的是右子树减去左子树的高度
 */
class AvlTree{
public:
    AvlTree():root(NULL){
        /*手动构造一棵AVL树 628579
        root = new AvlNode(6,0);
        root->left = new AvlNode(2,1);
        root->left->right = new AvlNode(5,0);
        root->right = new AvlNode(8,0);
        root->right->left = new AvlNode(7,0);
        root->right->right = new AvlNode(9,0);*/
    }
    ~AvlTree(){Empty(root);}
    void InorderTreeWalk(){
        InorderTreeWalk(root);
        cout<<endl;
    }
    AvlNode * Root(){
        return root;
    }
    void Insert(int x);
    bool Delete(int x);
    AvlNode * root;
    //左单旋转
    void RotateLeft(AvlNode * & n);
    //右单旋转
    void RotateRight(AvlNode * & n);
    //先左后右旋转
    void RotateLeftRight(AvlNode * & n);
    //先右后左旋转
    void RotateRightLeft(AvlNode * & n);
private:
    //求树的高度
    int Height(AvlNode * n);
    //清空所有元素
    void Empty(AvlNode * n);
    //中序遍历
    void InorderTreeWalk(AvlNode * n);
};
/**
 *左旋形状是”八“的右边 并不是指左单就只有左树
 *旋转时 确定中间大小的元素是重点
 *把中间的元素作为父 小的左子 大的右子
 *指针最初应该是指向的 最顶上元素
 */
void AvlTree::RotateLeft(AvlNode * & n){
   /**
    * n最小将旋钻到左下
    */
   AvlNode * l = n;
   n = n->right;//原右节点上升
   l->right = n->left;//右节点的左子成为 最小的右子
   n->left = l;//原右节点左子 为最小元素
   l->bf = n->bf = 0;//改变节点的高度
}
void AvlTree::RotateRight(AvlNode * & n){
    /**
     * 右旋 形状是八的左边
     */
    AvlNode * r = n;
    n = n->left;
    r->left = n->right;
    n->right = r;
    r->bf = n->bf = 0;
}
void AvlTree::RotateLeftRight(AvlNode * & n){
    /**
     * 先左后右的旋转 形状是“女”的第一划
     */
    AvlNode * r = n, * l = n->left;
    n = l->right;
    //因为新n要成为根 需要把左子正确移动
    l->right = n->left;
    n->left = l;
    //原本新n的bf=0,由于在新n左树插入节点,导致左树高度加一
    if(n->bf <=0)
        l->bf = 0;
    else//在新n的右树插入节点
        l->bf = -1;
    //需要将新n的右子正确移动
    r->left = n->right;
    n->right = r;
    if(n->bf == -1)
        r->bf = 1;
    else//新节点加在了右子上
        r->bf = 0;
    n->bf = 0;
}
void AvlTree::RotateRightLeft(AvlNode * & n){
    /**
     * 先右旋再左旋 形状是“女”的非第一划
     */
    AvlNode * l = n, * r = n->right;
    n = r->left;
    //正确移动右子
    r->left = n->right;
    n->right = r;
    if(n->bf >= 0)//新节点加在右子
        r->bf = 0;
    else
        r->bf = 1;
    //正确移动左子
    l->right = n->left;
    n->left = l;
    if(n->bf == 1)//加在了右树
        l->bf = -1;
    else
        l->bf = 0;
    n->bf = 0;
}
int AvlTree::Height(AvlNode * n){
    return 0;
}
void AvlTree::Insert(int x){
/**
 *插入新节点后 会导致根到该节点的bf被修改 因此要从节点到根回溯
 *新节点的bf=0 考虑插入父节点的左右子树
 *插入后 父节点bf=0 那么是插入到了父节点的较矮树上 路径上bf不变 不必处理
 *父节点的bf=1 说明原父节点bf=0 可能会导致路径上出现不平衡
 *父节点bf=2 说明是在父节点的较高树插入了元素
 * 考察 p父节点 q父节点直接子女 x新节点q的子女
 * 若bf=2 右高 q=1 左单
 *             q=-1 先右后左
 *若bf=-2 左高 q=-1 右单
 *             q=1 先左后右
 *旋转后 bf等于2的高度将为原本平衡的1 因此不需要再进行回溯平衡
 */
    AvlNode * p = root, * pr = NULL/*路径上除待插入节点的节点
    停在待插入父节点上*/ ,* q=NULL/*pr父节点*/;
    AvlStack as;
    //查找x应该插入的节点 但并不进行插入
    //cout<<"install insert: ";
    while(p){
        pr = p;
        as.Push(p);
        if(x<p->key)
            p = p->left;
        else
            p = p->right;
    }
    as.output();
    //创建插入节点
    p = new AvlNode(x,0);
    if(pr==NULL){
        root = p;//说明是空树 把新节点赋值给根即可
        return;
    }
    /*将节点正确的插入到树中*/
    if(x<pr->key)
        pr->left = p;
    else
        pr->right = p;
    while(as.IsEmpty() == false){
        pr = as.Pop();
       // cout<<pr<<":"<<pr->key<<endl;
        //调整父节点的平衡因子
        if(p == pr->left)
            pr->bf--;
        else
            pr->bf++;
        if(pr->bf == 0)
            break;//出现第一种情况 bf=0
        if(pr->bf == 1 || pr->bf == -1){
            p = pr;//出现第二种情况 向上回溯即可
        }else{//第三种情况
            // 首先出现第二种情况 然后上升一个节点 出现第三种情况
            //按照上面第三种情况判断
            if(pr->bf>0){//右高
                if(p->bf==1)
                    RotateLeft(pr);
                else
                    RotateRightLeft(pr);
            }else{
                if(p->bf==-1)
                    RotateRight(pr);
                else
                    RotateLeftRight(pr);
            }
            break;
        }
    }
    //当回溯到根了 第二种情况
    if(as.IsEmpty()==true){
        root = pr; // 最后弹出来的一定是根 所以这句可以不要
    }else{
        q = as.GetTop();
        if(q->key>pr->key)
            q->left = pr;
        else
            q->right = pr;
    }
    cout<<endl;
}
bool AvlTree::Delete(int x){
    AvlNode * pr = NULL, * p = root;
    AvlStack as;/*记录搜索的路径*/
    //首先查找节点的位置
    while(p){
        if(p->key == x)
            break;//找到了元素
        as.Push(p);
        pr = p;
        if(x<p->key)
            p = p->left;
        else
            p = p->right;
    }
    if(!p)
        return false;//到了nil节点还没有找到
    AvlNode * q;
    if(p->left && p->right){
        //有两个子女的情况 变为删除其后继 然后用前区或后继的数据
        //替换它的数据
        //前驱是左子树的最大者 就是最右边的节点
        pr = p;
        as.Push(p);
        q = p->left;
        while(q->right){
            pr = q;
            as.Push(q);
            q = q->right;
        }
        p->key = q->key;//交换数据
        /*此时q即为前驱元素 后继的话 就是右子树的最大者即最左边*/
        p = q;/*待删除的节点重新赋值给q*/
    }
    /*此时被删除的元素都转化为只有一个节点了 */
    if(p->left)
        q = p->left;
    else if(p->right)
        q = p->right;//q记录被删除节点的为一子女
    else
        q = NULL;/*p是没有子女的节点 考察其父节点*/
    if(pr == NULL){
        root = q;//被删除节点为根 则q成为新根
    }else{
        /*删除p pr q之间的链接
         *还要正确标识 p是pr的左还是右节点才行 要不q为空会导致错误
         * */
        bool pLeft = true;
        if(pr->left == p){
            pr->left = q;
            pLeft = true;
        }else{
            pr->right = q;
            pLeft = false;
        }
        int d;//用来标识q 这个较高的子树是左子树 还是右子树
        //重新平衡树

        while(as.IsEmpty()==false){
            pr = as.Pop();
            if(q){
                if(pr->right == q)
                    pr->bf--;/*p是右子但被删除了 应该减一*/
                else
                    pr->bf++;
            }else if(pLeft){
                    /*当第一次回溯时 如果p没有子女的话 将q赋值给pr
                    * 而pr又只有一个p子女的时候
                    * 会导致将无法比较删除掉的原本是左支还是右支
                    * 进而会错误的导致bf的改变
                    *  但是进行第二次回溯时q就一定不是空了*/
                    pr->bf++;
            }else{
                    pr->bf--;
            }
            int dd=0;
            AvlNode * ppr;
            if(as.IsEmpty()==false){
                ppr = as.GetTop();
                dd = (ppr->left==pr) ? -1 : 1;
            }
            //pr 的高度没有发生改变 停止平衡
            if(pr->bf==1 || pr->bf==-1)
                break;
            if(pr->bf==2 || pr->bf==-2){/*当出现不平横 需要进行平衡操作*/
                //使用q指示较高的子树
                if(pr->bf<0){
                    q = pr->left;
                    d = -1;
                }else{
                    q = pr->right;
                    d = 1;
                }
                if(q->bf==0){/*当q的bf为零 进行一次单旋即可*/
                    if(d==-1){//当q是左树的 进行右单旋转
                        RotateRight(pr);
                        pr->bf = 1;
                        q->bf = -1;
                    }else{
                        RotateLeft(pr);
                        pr->bf = -1;
                        q->bf = 1;
                    }
                    break;
                }
                if(q->bf == d){//同号说明是单旋转
                    if(d== -1)
                        RotateRight(pr);
                    else
                        RotateLeft(pr);
                }else{//不同号 那么就要进行双旋转了
                    if(d==-1)//先左后右
                        RotateLeftRight(pr);
                    else
                        RotateRightLeft(pr);
                }
                if(dd==-1)
                    ppr->left = pr;
                else if(dd==1)
                    ppr->right = pr;
            }
            q = pr;
        }
        if(as.IsEmpty()==true)//回溯到根了
            root = pr;
    }
    delete p;
    return true;
}
void AvlTree::Empty(AvlNode * n){
    if(n){
        Empty(n->left);
        Empty(n->right);
        delete n;
    }
}
void AvlTree::InorderTreeWalk(AvlNode * n){
    if(n){
        InorderTreeWalk(n->left);
        cout<<n->key<<" ";
        InorderTreeWalk(n->right);
    }
}
int main(void){
    AvlTree avl;
    avl.Insert(9);
    avl.Insert(5);
    avl.Insert(7);
    avl.Insert(2);
    avl.Insert(8);
    avl.Insert(1);
    avl.InorderTreeWalk();
    avl.Delete(7);
    avl.InorderTreeWalk();
    avl.Delete(1);
    avl.InorderTreeWalk();
    //先删除7再删除1会出错 已经修正于2014-4-11
/*    avl.InorderTreeWalk();
    avl.RotateLeft(avl.root);
    cout<<avl.root->key<<endl;
    avl.InorderTreeWalk();
    avl.RotateRight(avl.root);
    cout<<avl.root->key<<endl;
    avl.InorderTreeWalk();
    avl.RotateLeftRight(avl.root);
    cout<<avl.root->key<<endl;
    avl.InorderTreeWalk();
//    AvlStack as;
//    avl.RotateRightLeft(avl.root);
//    avl.InorderTreeWalk();*/
    return 0;
}

AVL树的时间复杂度分析:由于也是一棵搜索树,因此复杂度跟树的高度有关,插入查找删除均为O(log2n)。 经分析,AVL是可以包涵相同值得节点的,但是搜索和删除会导致不知道删除哪一个,所以我们在插入的时候,如果节点的值已经有了,我们就不用插入了。