平衡二叉树

来源:互联网 发布:淘宝男生服装店推荐 编辑:程序博客网 时间:2024/05/17 16:43

      平衡二叉树(balanced binary tree ),又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。构造与调整方法 平衡二叉树的常用算法有红黑树、AVL、Treap等。

      平衡二叉树是二叉排序输的变种,其目的就是为了改进二叉排序树在插入有序元素时退变成单链表而导致查找的时间复杂度增大的不稳定性,所以AVL树的基本操作还是:查找,插入,删除等,与二叉查找树不同的是AVL树借用旋转操作,在删除或者插入后都借用旋转操作来使AVL树保持平衡,

       二叉查找树的实现神很简单的,AVL树我们也从简单的说起,

先定义AVL树的节点类型:

template<class T>struct Node{    T     data;   //节点数据    int   h;    int freq ;     //平衡因子    Node* lson;   //左孩子    Node* rson;   //右孩子};

一、查找操作;

这算是AVL树最简单的操作了,和二叉查找树的查找操作完全一样,下面直接给出查找函数代码:

//递归实现

template<class T>void searchAVL(Node<T>* root ,Node<T>* res, T key){    if(root->data == key){        res = root;        return ;    }    if(root != NULL ){        searchAVL(root->lson ,res, key);        searchAVL(root->rson ,res, key);    }}

二、旋转操作:

总结不平衡的情况可以归纳未下面四种

1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左

2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右

3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左

4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右

从图中可以可以看出,(1)和(4)两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。(2)和(3)两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。

根据二叉排序树的节点特点,其代码实现也是很好办的:

LL型:

template<class T>void RotationLL(Node<T>*& A){    Node<T>* p= A->lson;    A->lson= p->rson;    p->rson= A;    A->h =max(A->lson->h, A->rson->h)+1;  // 高度更新    p->h =max(p->lson->h, p->rson->h)+1;    A=p; // 头结点更新}

RR型:

template<class T>void RotationRR(Node<T>*& A){    Node<T>* p= A->rson;    A->rson= p->lson;    p->lson= A;    A->h =max(A->lson->h, A->rson->h)+1;  // 高度更新    p->h =max(p->lson->h, p->rson->h)+1;    A=p; // 头结点更新}

说完了(1)、(4)我们继续说(2)、(3)

左右选旋转和右左旋转是对称的,这里详细说左右旋转,

旋转共分为两步,如图例:

牢记一句话:“根节点开始,左子树中所有节点小于根节点,右子树种所有节点大于根节点”,看图例就会觉得很简单了,

于是呢,LR旋转的代码我们就可以看着图片yy出来了:

template<class T>void RotaionLR(Node<T>*& X){    Node<T>* Y = X->lson;    Node<T>* Z = Y->rson;    X->lson = Z;    Y->rson = Z->lson;    Z->lson = Y;  //第一步构建完成    X->lson = Z->rson;    Z->rson = X;  //第二步构建完成    X= Z;  // 更新头结点    X->h = max(X->lson->h, X->rson->h )+1; //更新高度    Y->h = max(Y->lson->h, Y->rson->h )+1;    Z->h = max(Z->lson->h, Z->rson->h )+1;}


同样的道理,画个图,看着图片一步一步的写,于是RL旋转的代码也出来了:

template<class T>void RotaionRL(Node<T>*& X){    Node<T>* Y = X->rson;    Node<T>* Z = Y->lson;    X->rson = Z;    Y->lson = Z->rson;    Z->rson = Y;  //第一步构建完成    X->rson = Z->lson;    Z->lson = X;    //第二步构建完成    X= Z;   // 更新头结点    X->h = max(X->lson->h, X->rson->h )+1; //更新高度    Y->h = max(Y->lson->h, Y->rson->h )+1;    Z->h = max(Z->lson->h, Z->rson->h )+1;}

(当然LR函数,和RL函数其实就是LL和RR函数的组合,其实在LR中直接调用RR和LL进行旋转会使代码更简洁,这里是为了方便看着图例更好理解才这么写的)

有了旋转的函数那插入很删除就不是问题了,插入的的过程就是:

①按二叉排序树插入方式,先将节点插入到其应该被插入的位置;

②沿着插入节点到根节点的路径,依次选择旋转方式,通过旋转消除因插入节点而引起的不平衡

说着很简单的样子,用递归实现代码其实也不难:

template <class T>void insertAVL(Node<T>*& root , T key){    if(root == NULL ){        root = new Node<T>();        root->data = key;        root->h =1;        root->lson = root->rson = NULL;        return ;    }    if(root->data > key ){        insertAVL( root->lson  , key);        if( 2==(root->lson->h - root->rson->h) ){            if(key < root->lson->data ) //左左                RotationLL(root );            else                RotationLR(root);    //左右        }    }    if(root->data < key ){        insertAVL(root->rson, key );        if(2 ==(root->rson->h - root->lson->h )){            if(key > root->rson->data) //右右                RotaionRR(root);            else                RotaionRL(root);  //右左        }    }    else{        root->freq+=1;        return ;    }    root->h=max(root->lson->h ,root->rson->h)+1;    return;}

那么接下来就是删除了,删除和插入很类似,具体步奏如下:

  ①按二叉排序树删除方式,先将待删除节点删除;

  ②沿着被删除节点的父节点到根节点的路径,依次选择旋转方式,通过旋转消除因删除节点而引起的不平衡

递归实现代码如下:

<pre name="code" class="cpp">template <class T>void deleteAVL(Node<T>*& root, T key){    if(root == NULL )        return ; // 节点不存在,直接返回    else if(key < root->data ){        deleteAVL(root->lson, key);        if(2 == (root->rson->h - root->lson->h ) )             if( root->rson->lson!=NULL && (root->rson->lson->h) > (root->rson->rson->h) )                RotaionRL(root) ;            else                RotaionRR(root) ;    }    else if(key > root->data ){        deleteAVL(root->rson, key);        if(2 ==(root->lson->h - root->rson->h ) )            if(root->lson->rson!=NULL && (root->lson->rson->h ) > (root->lson->lson->h ) )                RotaionLR(root);            else                RotaionLL(root);    }else{        //找到节点了,删除方式有三种情况        //该节点为叶子节点        //左为空或右为空        //左右均不空        //合并为两种方式         if(root->lson && root->rson)  //此节点有两个儿子        {            Node<T>* temp=root->rson;  //temp指向节点的右儿子            while(temp->lson!=NULL ) temp=temp->lson;//找到右子树中值最小的节点            //把右子树中最小节点的值赋值给本节点            root->data=temp->data;            root->freq=temp->freq;            deleteAVL(root->rson,temp->data);//删除右子树中最小值的节点            if(2==(root->lson->h - root->rson->h )){                if(root->lson->rson != NULL && (root->lson->rson->h ) > (root->lson->lson->h ) )                    RotaionLR(root);                else                    RotaionLL(root);            }        }        else//此节点有1个或0个儿子        {            Node<T>* temp=root;            if(root->lson==NULL)//有右儿子或者没有儿子            root=root->rson;            else if(root->rson==NULL)//有左儿子            root=root->lson;            delete(temp);            temp=NULL;        }    }    root->h=max(root->lson->h,root->rson->h)+1;//调整更节点高度    return;}

欢迎大神指正。。。


















1 0
原创粉丝点击