平衡二叉树实现(1)

来源:互联网 发布:怎样训练狗狗捡球 知乎 编辑:程序博客网 时间:2024/06/04 17:50
1.什么是AVL树

AVL 树是一种二叉排序树,但是它能保持自身的高度平衡,这使得的树的查找与插入都很快,当然为了维持树的平衡在树节点插入与删除过程中也要做一些维持树本身平衡的操作。AVL树是由前苏联人G.M. Adelson-Velskii and E.M. Landis 1962年共同发明的,这种结构是计算机科学中发明的第一个有自平衡特性的数据结构,有着开创意义,为后来发明的2-4树、红黑树,AA树等指出了方向,这种设计思想有着很重要的意义。因为后来设计的更加复杂的数据结构如红黑树在平均性能上超过了AVL树,因此AVL树直接应用场合己不多见,但是学习它其中的优秀设计思想对提高自己的水平还是有很重要的意义;

2.平衡二叉树的插入操作

2.1最小不平衡树的根节点

最小不平衡树的根结点:也就是当你进行插入操作时,找到该需要插入结点的位置并插入后,从该结点起向上寻找(回溯),第一个不平衡的结点即平衡因子bf变为-2或2。

当平衡的二叉排序树因结点而失去平衡时,仅需要对最小不平衡的子树进行平衡旋转即可。因为经过旋转处理之后的子树深度和插入之前的相同,因而不影响插入路径上所有祖先结点的平衡度。

2.2 平衡二叉树的插入操作

平衡二叉树的操作,采用递归的方式,便于从底部开始上溯,发现最小不平衡树的根节点;

(1)插入操作首先从根节点开始,比较插入值和结点值,如果相等,则返回,如果小于,则递归根节点的左子树,否则递归右子树。

(2)树中不存在插入结点时,通过改变原树,叶子结点的左右指针,来插入新的结点。并标识结点的高度增加。

(3)递归返回,从叶子结点开始上溯,根据插入情况(插入左子树,平衡因子加1,插入右子树平衡因子减1)依次改变父结点的平衡因子。

(4)如果结点的平衡因子为2,或-2,则按照2.3所述的方法对该节点进行旋转,同时标示高度为增加;

结点插入操作的代码如下所示:

/************************************************************************//* 平衡二叉树,插入结点                                                 *//************************************************************************/int insertnode(pnode * ptree, nodetype key, int * taller){//根据新增结点的值,改变叶子节点的左或右指针if (!(*ptree)){*ptree = (pnode)malloc(sizeof(pnode));if (!(*ptree)){debug("alloc node failed\n");}debug("alloc success\n");(* ptree)->key = key;(* ptree)->bf = EN;(* ptree)->rchild = (* ptree)->lchild = NULL;*taller  = 1;}//节点值相等,直接返回,结点高度未增加else if ((* ptree)->key == key){debug("find the node\n");* taller = 0;return 0;}else if ((* ptree)->key > key){//递归调用插入操作,保证从最小不平衡子树的根结点开始if (insertnode(&(* ptree)->lchild, key, taller) ==0)return 0;//左子树插入结点,高度增加,改变平衡因子。if (*taller){switch((*ptree)->bf) { //原来结点左子树高,平衡处理case LH:printf("左平衡");                leftblance(ptree);* taller = 0;break;//原来结点平衡,结点平衡度为1.case EN:(*ptree)->bf = LH;* taller = 1;break;//原来结点右子树高,结点平衡度为0case RH:(*ptree)->bf = EN;*taller = 0;break;default:break;}}}else{//递归调用插入操作,保证从最小不平衡子树的根结点开始if (insertnode(&(* ptree)->rchild, key, taller) ==0)return 0;//右子树插入,结点高度增加,改变平衡因子if(*taller){               switch((*ptree)->bf){ //结点左子树高,结点平衡因子为0            case LH:(*ptree)->bf = EN;*taller = 0;            break;    //结点平衡,结点平衡因子为-1case EN:(*ptree)->bf = RH;*taller = 1;break;//结点右子树高,右平衡case RH:printf("右平衡");rightblance(ptree);*taller = 0;break;            default:break;            }}}return 1;}

2.3 平衡二叉树结点的旋转,

旋转主要通过改变指针的指向和相关结点的平衡因子实现。

2.3.1 改变指针实现旋转

根据旋转的情况,分为左旋转和右旋转。

1.左旋转:根节点成为根右孩子的左孩子。右孩子成为根结点。

                  实现步骤:1.保存指向右孩子的指针;2。根节点的右指针指向右节点的左孩子。3.根节点的右孩子的做指针指向根节点。4.根节点的指针指向根节点的右孩子。 

                  图示如下:

 

代码如下所示:

/************************************************************************//* 结点左旋转                                                           *//************************************************************************/void left_rotate(pnode * pn){   //保存根节点右指针    pnode pr= (* pn)->rchild;//根节点的右指针指向右孩子的左孩子(*pn)->rchild = pr->lchild;//右孩子的左指针指向根节点pr->lchild = (*pn);//根节点指向右孩子(*pn) = pr;}

2.右旋转

右旋转与左旋转一样,步骤参见代码

/************************************************************************//* 结点右旋转                                                         *//************************************************************************/void right_rotate(pnode * pn){     //保存根节点的做指针pnode pl= (*pn)->lchild;//根节点的左指针,指向左孩子的右孩子(*pn)->lchild = pl->rchild;//右孩子的做指针指向根节点pl->rchild = *pn;//根节点指向左孩子*pn = pl;}/************************************************************************/

3.根据结点的插入情况:旋转类型分为4种,操作方法如下表所示

操作类型

操作方法

左左:左子树插入左节点根节点右旋转左右:左子树插入右结点先根节点的左孩子左旋转,然后根节点右旋转右右:右子树插入右结点根节点左旋转右左:右子树插入左节点先根节点的右孩子右旋转,然后根节点左旋转代码如下所示:

/************************************************************************//* 左平衡                                                               *//************************************************************************/void leftblance(pnode * pn){pnode pr;pr =(*pn)->lchild;switch(pr->bf) {case LH://左左printf("右旋转");(*pn)->bf = (*pn)->lchild->bf = EN;//根节点右旋转right_rotate(pn);break;case RH: //左右switch((*pn)->lchild->rchild->bf){case LH:(*pn)->bf = RH;(*pn)->lchild->bf = EN;break;case EN:(*pn)->bf = (*pn)->lchild->bf = EN;break;case RH:(*pn)->bf = EN;(*pn)->lchild->bf = LH;break;default:break;}(*pn)->lchild->rchild->bf = EN;//左孩子左旋left_rotate(&((*pn)->rchild));//根节点右旋right_rotate(pn);break;default:break;}}/************************************************************************//* 右平衡                                                               *//************************************************************************/void rightblance(pnode * pn){pnode pr;pr = (*pn)->rchild;switch(pr->bf){//右右:右孩子插入右结点case RH:(*pn)->bf = (*pn)->rchild->bf = EN; left_rotate(pn);break;//右左:右孩子插入左节点case LH:         switch((*pn)->rchild->lchild->bf) {         case LH: (*pn)->bf = EN; (*pn)->rchild->bf = RH;         break; case EN: (*pn)->bf = (*pn)->rchild->bf = EN; break; case RH: (*pn)->bf = LH; (*pn)->rchild->bf = EN; break;         default: break;         }(*pn)->rchild->lchild->bf = EN;        //右孩子,右旋right_rotate(&((*pn)->rchild));//根节点,左旋left_rotate(pn);break;default:break;}}


2.3.2 结点平衡度的改变,见下一博客;