数据结构之平衡二叉树

来源:互联网 发布:淘宝 国际米兰球衣 编辑:程序博客网 时间:2024/04/30 09:48

            小目录

             1.前言

             2.平衡二叉树的插入

             3.平衡二叉树的查找

             4.平衡二叉树查找性能分析



         1.前言

          前言就是这玩意真蛋疼,比B树感觉理解难度大。平衡二叉树或者是一棵空树,或者是具有下列性质的二叉排序树:它的左子树和右子树都是平衡二叉树,且左子树和右子树高度之差的绝对值不超过1。

         将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子 BF ,那么平衡二叉树上所有结点的平衡因子只可能是一 1 、 0 和 1. 只要二叉树上有一个结点的平衡因子的 绝对值大于 1 ,则 该二叉树就是不平衡的(如图)。

                                  


       2.平衡二叉树的插入

       在平衡二叉树上插入或删除结点后,可能使树失去平衡,因此,需要对失去平衡的树进行平衡化调整(这就是蛋疼之所在),这里仅仅是插入的介绍。但主要思想还是有的(我乱说的):在插入前查找树中是否存在该节点,不存在进行相应的插入,否则不插入。在插入的过程根据情况进行左右单旋转或是双旋转,由于具体的过程羞涩难懂,这里就直接上书上的说法和图: 

                           


       这里做简单说明:a需要进行单右旋,b需要先进行左旋然后进行右旋,c需要进行单左旋,d需要先进行右旋然后进行左旋。

      具体的插入操作实现代码如下(当时看到这代码我就崩溃了,其中有一些不是很明白已经注释了,如果有明白的求大神教我啊):

/** @description:插入平衡二叉树中不存在的元素值,并保证树的平衡性* @more:这个真心问候很多人的祖宗* @param Status *taller 用于判断是否有新节点加入树中*/Status InsertAVL(BiTree *T,TElemType key,Status *taller) {//当树或者子树为空的情况,则需要生成新节点if(!(*T)) {*T = (BiTree)  malloc(sizeof(struct BiTNode));(*T)->lchild = (*T)->rchild = NULL;(*T)->data = key;//标记有新节点加入*taller = TRUE;(*T)->bf = EH;}else {//树中已经存在相同的节点值则不插入if((*T)->data == key) {*taller = FALSE;return FALSE;}//当前节点的值大于key则说明应该往左边找或插入if((*T)->data > key) {//没有插入则返回FALSEif(!InsertAVL( &(*T)->lchild,key,taller )) return FALSE;//已经插入到左边且左边增高if(*taller) {switch((*T)->bf) {//原本左子树比右子树高则在往左边加入新界点后,则需要做左平衡case LH:LeftBalance(T);*taller = FALSE;break;//原本左右子树高度相等,加入新节点后,现在左子树高于右子树case EH:(*T)->bf = LH;*taller = TRUE;break;//原本右子树高于左子树,加入新节点后,现在左右子树高度相等case RH:(*T)->bf = EH;*taller = FALSE;break;}}}else {if(! InsertAVL(&(*T)->rchild,key,taller)) {*taller = FALSE;return FALSE;}//成功往右插入节点,且右边增高if(*taller) {switch((*T)->bf) {//原来左子树高于右子树,现在加入新节点后,左右子树高度相等case LH:(*T)->bf = EH;*taller = FALSE;break;//原来左右子树高度相等,现在加入新节点后,右子树高度高于左子树case EH:(*T)->bf = RH;*taller = TRUE;break;//原来右子树高于左子树,现在往右加入新节点后,需要做右平衡case RH:RightBalance(T);*taller = FALSE;break;}}}}return TRUE;}/** @description:左平衡-->旋转处理*/Status LeftBalance(BiTree *T) {BiTree lc,rd;//指向*T的左子树根节点lc = (*T)->lchild;switch(lc->bf) {//新节点插在了*T的左孩子的左子树,则需要做单右旋处理case LH:(*T)->bf = lc->bf = EH;R_Rotate(T);break;//新节点插在了*T的左孩子的右子树上,则需要双旋处理,先左后右case RH:rd = lc->rchild;//关于LH和RH的情况不是明白,有明白的求教switch(rd->bf) {/*case LH:(*T)->bf = RH;lc->bf = EH;break;*/case EH:(*T)->bf = lc->bf = EH;break;/*case RH:(*T)->bf = EH;lc->bf = LH;break;*/}rd->bf = EH;//先做左旋转,针对*T的左孩子L_Rotate(&(*T)->lchild);//对*T做右旋转R_Rotate(T);break;}return TRUE;}/** @description:右平衡*/Status RightBalance(BiTree *T) {BiTree lc,rd;//指向*T的右孩子lc = (*T)->rchild;switch(lc->bf) {case RH:(*T)->bf = lc->bf = EH;L_Rotate(T);break;//新节点插入在*T的右孩子的左子树上case LH://指向*T的右孩子的左孩子rd = lc->lchild;switch(rd->bf) {/*case LH:(*T)->bf = EH;lc->bf = RH;break;*/case EH:(*T)->bf = lc->bf = EH;break;/*case RH:(*T)->bf = LH;lc->bf = EH;break;*/}rd->bf = EH;//先将*T的右孩子为根节点的子树右旋转R_Rotate(&(*T)->rchild);//再左旋L_Rotate(T);break;}return TRUE;}/** @description:左旋转*/void L_Rotate(BiTree *p) {BiTree lc;lc = (*p)->rchild;//这一行的作用不明白(*p)->rchild = lc->lchild;lc->lchild = *p;//将*p重新调整*p = lc;}/** @description:右旋转*/void R_Rotate(BiTree *p) {BiTree lc;//lc指向*p的左孩子lc = (*p)->lchild;//这一行的作用不明白(*p)->lchild = lc->rchild;//*p成为lc的右孩子lc->rchild = *p;//将*p指向调整*p = lc;}



      3.平衡二叉树的查找

      查找的算法和二叉排序树的查找是一样的,具体代码如下:

/** @description:平衡二叉树查找*/BiTree SearchTree(BiTree T,TElemType key) {//找到if(!T || T->data == key)return T;//往左子树找else if(T->data > key)return SearchTree(T->lchild,key);//往右子树找elsereturn SearchTree(T->rchild,key);}



        4.平衡二叉树查找性能分析

        在平衡树上进行查找的过程和二叉排序树相同,因此,在查找过程中和给定值进行比较的关键码个数不超过树的深度。那么,含有n 个关键码的平衡树的最大深度是多少呢?为解答这个问题,我们先分析深度为h 的平衡树所具有的最少结点数。假设以Nh 表示深度为h 的平衡树中含有的最少结点数。显然,N0=0,N1=1,N2=2,并且Nh=Nh-1+Nh-2+1。这个关系和斐波那契序列极为相似。利用归纳法容易证明:当h≥0间复杂度为O(logn)。


最后附上完整代码地址:GitHub


Done


0 0