国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php开源 > php教程 > 平衡二叉树

平衡二叉树

来源:程序员人生   发布时间:2015-05-22 08:44:45 阅读次数:3350次

平衡2叉树又称为AVL树,

AVL树是最早发明的自平衡2叉查找树。在AVL树中任何节点的两个儿子子树的高度最大差别为1,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过1次或屡次树旋转来重新平衡这个树。

保护平衡2叉树需要有4种旋转的情况

左左旋转

右右旋转

左右旋转

右右旋转

//******************************************************************************************** //***说明:2叉排序树遍历是从小到大的顺序,AVL树也是bst,但是满足所有的树的左右高度相差不大于1 //***平衡2叉树是1种特殊的2叉树,满足以上要求,主要是为了进1步的优化,提高操作的效力(O(log(n))) //***针对下面情况进行调剂 //***测试pat1066 http://www.patest.cn/contests/pat-a-practise/1066 //*********1*******************************1**********2*************************************** //**************************************************/*************************************** //***********2**************************************1***4************************************* //*******************------>**************************/************************************* //*************3**************************************3***5*********************************** //**************4***************************************************************************** //******************************************************************************************* //****************5*************************************************************************** // 其中主要的策略就是选择,旋转1共分为下面4种情况 //******** A ********** B ************** C ********************* D *************************** //******************************************************************************************** //***** 6 **** 6 ************2***********************2**************************** //***** / **** / ***********/**********************/**************************** //***** 3 7 **** 2 7 **********1***5*******************1***4************************** //***** / **** / *************/**********************/************************** //*****1 4 **** 1 4 ************3***6*******************3***6************************ //***** **** / **************************************/************************* //***** 2 **** 3 **************4***********************5************************** //******************************************************************************************** //******************************************************************************************** //******************************************************************************************** //******************************************************************************************** //******************************************************************************************** /********************************************************************************************* *A、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这类情况成为左左。 *B、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这类情况成为左右。 *C、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这类情况成为右左。 *D、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这类情况成为右右。 * *说明:2叉排序树遍历是从小到大的顺序,AVL树也是bst,但是满足所有的树的左右高度相差不大于1 */ #ifndef AVL_H #define AVL_H #include <iostream> using namespace std; /*AVL树节点信息*/ template <typename T> class TreeNode { public : TreeNode():lchild(NULL),rchild(NULL),freq(1),hgt(0){} T data ;/*值*/ int hgt;/*以此节点为根的树的高度*/ unsigned int freq;/*频率*/ TreeNode * lchild,*rchild;/*左右孩子*/ }; /*AVL树类的属性和方法声明*/ template <typename T> class AVLTree { private : TreeNode<T> *root; /*根节点*/ void InsertPri(TreeNode<T> * &node, T x); /*插入x*/ TreeNode <T>* FindPri(TreeNode<T> *node ,T x); /*查找x*/ void InSubTree(TreeNode<T> *node); /*中序遍历*/ void DeletePri(TreeNode<T> * &node, T x); /*删除*/ int height(TreeNode<T> *node); /*树的高度*/ void SingRotateLeft(TreeNode<T> * &k2); /*左左情况下的旋转*/ void SingRotateRight(TreeNode<T> * &k2); /*右右情况的旋转*/ void DoubleRotateLR(TreeNode<T> * &k3); /*左右情况的旋转*/ void DoubleRotateRL(TreeNode<T> * &k3); /*左右情况的旋转*/ int Max(int cmpa,int cmpb); /*求最大值*/ public : AVLTree():root(NULL){} void insert(T x); /*插入接口*/ TreeNode<T> * Find(T x); /*查找接口*/ void Delete(T x); /*删除接口*/ void Treversal(); /*遍历接口*/ }; /*计算以节点为根的树的高度*/ template <typename T> int AVLTree <T>::height(TreeNode<T>* node) { return node!=NULL? node->hgt:⑴; } /*求最大值*/ template <typename T> int AVLTree<T>::Max(int cmpa,int cmpb) { return cmpa>cmpb ? cmpa :cmpb; } /************************************************************************/ /* 单旋转 单旋转是针对左左和右右这两种情况的解决方案,这两种情况是 *对称的,只要解决了左左这类情况,右右就很好办了。图3是左左情况的解决方案, *节点k2不满足平衡特性,由于它的左子树k1比右子树Z深2层,而且k1子树中,更深 *的1层的是k1的左子树X子树,所以属于左左情况 (左左情况下的选择) */ /*为使树恢复平衡,我们把k2变成这棵树的根节点,由于k2大于k1,把k2置于k1的右 *子树上,而本来在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既 *********************满足了2叉查找树的性质,又满足了平衡2叉树的性质。*/ /************************************************************************/ //****************k2******************************k1********************** //***************/******************************/*********************** //**************k1**z****--------->*************x***k2******************** //*************/**********************************/********************* //************x***y*******************************y***z******************* /*这样的操作只需要1部份指针改变,结果我们得到另外1颗2叉查找树,它是1棵AVL树,由于X向 *上1移动了1层,Y还停留在原来的层面上,Z向下移动了1层。整棵树的新高度和之前没有在左子 *树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根 *节点的路径就不需要继续旋转了。***********************************************/ /*左左旋转*/ template <typename T> void AVLTree<T>::SingRotateLeft(TreeNode<T> * &k2) { TreeNode<T> *k1; k1=k2->lchild; k2->lchild=k1->rchild; k1->rchild=k2; k2->hgt=Max(height(k2->lchild),height(k2->rchild))+1; k1->hgt=Max(height(k1->lchild),k2->hgt)+1; k2=k1;/*最后1定要更新根节点*/ } /*右右旋转*/ template <typename T> void AVLTree<T>::SingRotateRight(TreeNode<T> * &k2) { TreeNode<T> *k1; k1=k2->rchild; k2->rchild=k1->lchild; k1->lchild=k2; k2->hgt=Max(height(k2->lchild),height(k2->rchild))+1; k1->hgt=Max(height(k1->rchild),k2->hgt)+1; k2=k1;/*最后1定要更新根节点*/ } /**************************双旋转(左右情况下的双旋转)************************/ //************************************************************************ //*************k3******************k3**********************k2************* //************/******************/**********************/************** //**********k1***d*---->********k2***d*****---->********k1****k3********** //*********/*******************/*********************/****/*********** //********a****k2*************k1***c******************a***b*c***d********* //************/*************/******************************************* //***********b***c**********a***b***************************************** //************************************************************************ //************************************************************************ //************************************************************************ /************************************************************************/ /* *左右旋转 *为使树恢复平衡,我们需要进行两步,第1步,把k1作为根,进行1次右右旋转,旋转以后就变成 *了左左情况,所以第2步再进行1次左左旋转,最后得到了1棵以k2为根的平衡2叉树树。 */ template <typename T> void AVLTree<T>::DoubleRotateLR(TreeNode<T> * &k3) { SingRotateRight(k3->lchild); SingRotateLeft(k3); } /*右左旋转*/ template <typename T> void AVLTree<T>::DoubleRotateRL(TreeNode<T> * &k3) { SingRotateLeft(k3->rchild); SingRotateRight(k3); } /************************************************************************* *插入的方法和2叉查找树基本1样,区分是,插入完成后需要从插入的节点开始保护 *1个到根节点的路径,每经过1个节点都要保持树的平衡。 *保持树的平衡要根据高度差的特点选择不同的旋转算法。 *insert *************************************************************************/ template <typename T> void AVLTree<T>::InsertPri(TreeNode<T>* &node, T x) { if(node==NULL)/*如果节点为空,就在此节点处加入x信息*/ { node =new TreeNode<T>(); node->data=x; return ; } if (node->data>x)/*如果X小于节点的值,就继续再节点的左子树中插入*/ { InsertPri(node->lchild,x); if (2==height(node->lchild)-height(node->rchild)) { if (x<node->lchild->data) { SingRotateLeft(node); } else DoubleRotateLR(node); } } else if (node->data<x)/*如果X大于节点的值,就继续再节点的右子树中插入*/ { InsertPri(node->rchild,x); if (2==height(node->rchild)-height(node->lchild)) { if (x>node->rchild->data) { SingRotateRight(node); } else DoubleRotateRL(node); } } else node->freq++;/*如果相等,频率加1*/ node->hgt=Max(height(node->lchild),height(node->rchild))+1; } /************************************************************************/ /* 插入接口 */ /************************************************************************/ template <typename T> void AVLTree<T>::insert(T x) { InsertPri(root,x); } /************************************************************************/ /* 查找 */ /*和2叉查找树相比,查找方法没有变法,不过根据存储的特性, *AVL树能保持在1个O(logN)的稳定的时间,而2叉查找树则相当不稳定。 /************************************************************************/ template<typename T> TreeNode<T>* AVLTree<T>::FindPri(TreeNode<T> *node ,T x) { if(node==NULL)/*如果节点为空说明没找到,返回NULL*/ return NULL; if(node->data>x)/*如果x小于节点的值,就继续在节点的左子树中查找x*/ return FindPri(node->lchild,x); if (node->data<x)/*如果x大于节点的值,就继续在节点的左子树中查找x*/ return FindPri(node->rchild,x); return node;/*如果相等,就找到了此节点*/ } /*查找接口*/ template<typename T> TreeNode<T>* AVLTree<T>::Find(T x) { return FindPri(root,x); } /************************************************************************/ /* 删除 */ /*删除的方法也和2叉查找树的1致,区分是,删除完成后, *需要从删除节点的父亲开始向上保护树的平衡1直到根节点。 /************************************************************************/ template<typename T> void AVLTree<T>::DeletePri(TreeNode<T> * &node, T x) { if(node==NULL)/*空*/ return ; if(x<node->data) {/*如果x小于节点的值,就继续在节点的左子树中删除x,删除完成后,需要调剂树,使之保持平衡*/ DeletePri(node->lchild,x); if (2==height(node->rchild)-height(node->lchild)) { if(node->rchild->lchild!=NULL && (height(node->rchild->lchild)>height(node->rchild->rchild)) ) DoubleRotateRL(node); else SingRotateRight(node); } } else if (x>node->data) {/*如果大于节点的值,就继续在节点的右子树中删除x,删除完成后,需要调剂树,使之保持平衡*/ DeletePri(node->rchild,x); if (2==height(node->lchild)-height(node->rchild)) { if(node->rchild->lchild!=NULL && (height(node->lchild->rchild)>height(node->lchild->lchild)) ) DoubleRotateLR(node); else SingRotateLeft(node); } } else/*相等,删除,然后调剂,使之平衡*/ { if (node->lchild && node->rchild)/*此节点有两个儿子*/ { TreeNode<T>* temp=node->rchild;/*temp指向节点的右儿子*/ while(temp->lchild!=NULL) temp=temp->lchild;/*找到中序遍历的后继节点,一定在最左侧那个*/ node->data=temp->data;/*调剂节点数据信息*/ node->freq=temp->freq; DeletePri(node->rchild,temp->data);/*删除边沿节点*/ if (2==height(node->lchild)-height(node->rchild)) { if (node->lchild->rchild!=NULL && (height(node->lchild->rchild)>height(node->lchild->lchild)) ) DoubleRotateLR(node); else SingRotateLeft(node); } } else/*此节点有1个或0个儿子*/ { TreeNode<T> *temp=node; if(node->lchild==NULL)/*有右儿子或没有儿子*/ node=node->rchild; else if(node->rchild==NULL)/*有左儿子*/ node=node->lchild; delete(temp); temp=NULL; } } if(node==NULL) return ; node->hgt=Max(height(node->lchild),height(node->rchild))+1; return ; } /*删除接口*/ template<typename T> void AVLTree<T>::Delete(T x) { DeletePri(root,x); } /*中序遍历函数*/ template<typename T> void AVLTree<T>::InSubTree(TreeNode<T> *node) { if(node==NULL) return ; InSubTree(node->lchild);/*先遍历左子树*/ cout<<node->data<<" ";/*输出根节点*/ InSubTree(node->rchild);/*再遍历右子树*/ } /*中序遍历接口*/ template<typename T> void AVLTree<T>::Treversal() { //cout<<root->data; InSubTree(root); } #endif//avl.h

测试用例:

#include "AVL.h" int arrs[7]={88,70,61,96,120,90,65}; int main(int argc,char** argv) { AVLTree<int> te; for (int i=0;i<7;i++) { te.insert(arrs[i]); } te.Treversal(); return 0; }


生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生