平衡二叉树研究(AVL树)
2015-08-20 21:42
417 查看
最近在研究数据结构,使用教材是程杰的《大话数据结构》,这本树写的非常好,看到了平衡二叉树,对该章研究了下,敲了下代码,并通过加断点调试,学习了不少,记录下来,供大家参考。
(1)平衡二叉树(AVL树概念)
一种二叉排序树,其中每一个结点的左子树和右子树的高度差的绝对值小于等于1。
平衡因子BF:左子树深度减去右子树深度的值(有正有负)
最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。
(2)平衡二叉树实现原理
基本思想:当插入一个新的结点,出现不平衡情况时,就对最小不平衡子树进行调整。
调整平衡时主要是两种情况:
(a)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号同号,直接进行左旋或者右旋即可。
(b)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号异号,先对最小不平衡子树根节点的左右子树根节点进行旋转,使两者平衡因子符号由异号变为同号,然后对最小不平衡子树根节点进行旋转(其实就是进行两次旋转,双旋处理)。
(3)平衡二叉树实现
读代码的一点感悟:
(a)插入结点函数InsertAVL是一个递归插入操作,当新增一个结点成功后,然后退出当前的插入操作,递归回退到上一次插入操作,并对新插入结点的上一次结点的平衡因子值进行修改。
(b)直接阅读代码有些困难,最好就是在编译器中加断点进行调试,这样对于理解插入结点时,如何调整平衡的原理很有用。(我是这样的情况,纸上代码看了很久都没看懂,上机调试了一把,立马恍然大悟)
(c)结点删除时,由插入结点是递归,可以推测出删除时也用递归,可见递归操作很强大啊!二叉排序树删除结点三种情况:(a1)叶子结点(a2)仅有左或者右子树(a3)左右子树都存在。删除结点时,判断树的高度是否变矮。(a1)(a2)情况,树高度变矮了,(a3)情况,找到直接前驱,方法是"先向左,然后向右到尽头",将直接前驱的值付给根节点,然后在根节点左子树中递归删除直接前驱结点。
二叉链表结点结构体定义:
删除结点函数:
测试代码主函数:
打印结果:
(1)平衡二叉树(AVL树概念)
一种二叉排序树,其中每一个结点的左子树和右子树的高度差的绝对值小于等于1。
平衡因子BF:左子树深度减去右子树深度的值(有正有负)
最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。
(2)平衡二叉树实现原理
基本思想:当插入一个新的结点,出现不平衡情况时,就对最小不平衡子树进行调整。
调整平衡时主要是两种情况:
(a)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号同号,直接进行左旋或者右旋即可。
(b)最小不平衡子树,根结点平衡因子与左或者右子树根节点平衡因子符号异号,先对最小不平衡子树根节点的左右子树根节点进行旋转,使两者平衡因子符号由异号变为同号,然后对最小不平衡子树根节点进行旋转(其实就是进行两次旋转,双旋处理)。
(3)平衡二叉树实现
读代码的一点感悟:
(a)插入结点函数InsertAVL是一个递归插入操作,当新增一个结点成功后,然后退出当前的插入操作,递归回退到上一次插入操作,并对新插入结点的上一次结点的平衡因子值进行修改。
(b)直接阅读代码有些困难,最好就是在编译器中加断点进行调试,这样对于理解插入结点时,如何调整平衡的原理很有用。(我是这样的情况,纸上代码看了很久都没看懂,上机调试了一把,立马恍然大悟)
(c)结点删除时,由插入结点是递归,可以推测出删除时也用递归,可见递归操作很强大啊!二叉排序树删除结点三种情况:(a1)叶子结点(a2)仅有左或者右子树(a3)左右子树都存在。删除结点时,判断树的高度是否变矮。(a1)(a2)情况,树高度变矮了,(a3)情况,找到直接前驱,方法是"先向左,然后向右到尽头",将直接前驱的值付给根节点,然后在根节点左子树中递归删除直接前驱结点。
二叉链表结点结构体定义:
typedef struct BiTNode //结点结构体 { int data; int bf; //结点平衡因子 struct BiTNode *lchild,*rchild; }BiTNode,*BiTree;右旋操作:
/********************************************* **功能:对以p为根的二叉排序树做右旋处理 **p:二叉排序树 *********************************************/ void R_Rotate(BiTree *p) { BiTree L; L = (*p)->lchild; //L指向根节点的左子树 (*p)->lchild = L->rchild; //L的右子树挂在根节点的左子树上 L->rchild = (*p); (*p) = L; //p指向新的根节点 }左旋操作:
/********************************************* **功能:对以p为根的二叉排序树做左旋处理 **p:二叉排序树 *********************************************/ void L_Rotate(BiTree *p) { BiTree R; R = (*p)->rchild; (*p)->rchild = R->lchild; R->lchild = (*p); (*p) = R; }左平衡旋转处理:
#define LH 1 //左高 #define EH 0 //等高 #define RH -1 //右高 /********************************************* **功能:对指针T所指结点为根的结点进行左平衡旋转处理 **T:二叉排序树 *********************************************/ void LeftBalance(BiTree *T) { BiTree L,Lr; L = (*T)->lchild; //L指向左子树根节点 switch(L->bf) { case LH: //新节点插入在左孩子的左子树上,要作单右旋处理 (*T)->bf = L->bf = EH; R_Rotate(T); break; case RH: Lr = L->rchild; switch(Lr->bf) { case LH: (*T)->bf = RH; L->bf = EH; break; case EH: (*T)->bf=L->bf=EH; break; case RH: (*T)->bf = EH; L->bf = LH; break; } Lr->bf = EH; L_Rotate(&(*T)->lchild); //对T的左子树做左旋处理 R_Rotate(T); //对T做右旋处理 } }右平衡旋转处理:
/********************************************* **功能:对指针T所指结点为根的结点进行右平衡旋转处理 **T:二叉排序树 *********************************************/ void RightBalance(BiTree *T) { BiTree R,Rl; R = (*T)->rchild; switch(R->bf) { case RH: //此时,根节点和右子树结点平衡因子相同,直接对根节点左旋 (*T)->bf = R->bf = EH; L_Rotate(T); break; case LH: Rl = R->lchild; //指向右子树的根节点的左子树,异号,需要进行双旋处理 switch(Rl->bf) { case RH: (*T)->bf = LH; R->bf = EH; break; case EH: (*T)->bf = R->bf=EH; break; case LH:(*T)->bf = EH; R->bf = RH; break; } Rl->bf = EH; R_Rotate(&(*T)->rchild); //对T的右子树根节点做右旋操作 L_Rotate(T); //对根节点T做左旋操作 } }插入结点主函数:
/********************************************* **功能:平衡二叉树中插入一个结点e,若e不存在,插入并返回true,否则返回false **T:平衡二叉树 **e:带插入结点 **taller:树是否增高 *********************************************/ int InsertAVL(BiTree *T,int e,int *taller) { if(!*T) { *T =(BiTree)malloc(sizeof(BiTNode)); (*T)->data = e; (*T)->lchild = NULL; (*T)->rchild = NULL; (*T)->bf = EH; *taller = 1; //表示树长高了 } else { if (e == (*T)->data) //该节点已经存在 不用插入了 { *taller = 0; return 0; } if (e < (*T)->data) { if (!InsertAVL(&(*T)->lchild,e,taller)) //左子树中继续搜索 但是没有插入 { return 0; } if (*taller) //插入新的结点 并且树长高了 { switch((*T)->bf) { case LH: //原来左子树比右子树高,做左平衡处理 LeftBalance(T); *taller = 0; break; case EH: //原来左、右子树等高,现在左子树增高使树增高 (*T)->bf = LH; *taller = 1; break; case RH: //原来右子树较高,现在左右子树一样高 (*T)->bf = EH; *taller = 0; break; } } } else //右子树中进行搜索 { if (!InsertAVL(&(*T)->rchild,e,taller)) //右子树中未插入 { return 0; } if (*taller) { switch((*T)->bf) //检查T的平衡度 { case LH: (*T)->bf = EH; *taller = 0; break; case EH: (*T)->bf = RH; *taller = 1; break; case RH: //本来右子树比左子树高,还在右子树插入结点,需要做右平衡处理 RightBalance(T); *taller = 0; break; } } } } return 1; }打印二叉排序树:
/********************************************* **功能:递归打印二叉树,中序遍历 **T:二叉排序树 *********************************************/ void PrintBTree(BiTree T) { if (!T) //递归出口 return; PrintBTree(T->lchild); printf("%d\t",T->data); PrintBTree(T->rchild); }
删除结点函数:
/********************************************* **功能:平衡二叉树中进行删除,又该节点,删除并返回true,若没有该节点,返回false **T:平衡二叉树 **e:删除的结点键值 **shorter:树的高度是否降低 *********************************************/ int DeleteAVL(BiTree *T,int e,int* shorter) { //提示:插入结点使用的递归插入,可以联想到删除也是用的递归删除 if (!*T) //不存在该节点 { return 0; } else if ((*T)->data == e) //找到该节点 { BiTree q; if (!(*T)->lchild) //如果左子树为空,只需要重接右子树 { q = (*T); (*T) = (q->rchild); free(q); *shorter = 1; } else if (!(*T)->rchild) //右子树为空,只需要重接左子树 { q = (*T); (*T) = q->lchild; free(q); *shorter = 1; } else //左右子树都不为空 { q = (*T)->lchild; //先向左,然后向右到尽头 while(q->rchild) { q = q->rchild; } (*T)->data = q->data; DeleteAVL(&(*T)->lchild,q->data,shorter); //左子树中递归删除前驱结点 } } else if (e < (*T)->data) //左子树中递归查找 { if (!DeleteAVL(&(*T)->lchild,e,shorter)) //没找到 { return 0; } if (*shorter) //删除结点,而且高度降低 { switch((*T)->bf) { case LH: (*T)->bf = EH; *shorter = 1; break; case EH: (*T)->bf = RH; *shorter = 0; break; case RH: //右边本来就高 左边删除后,需要进行右平衡处理 RightBalance(T); if ((*T)->rchild->bf == EH) //画图看一下,很easy { *shorter = 0; } else { *shorter = 1; } break; } } } else //右子树中递归查找 { if (!DeleteAVL(&(*T)->rchild,e,shorter)) //没找到 { return 0; } if (*shorter) //删除结点,而且高度降低 { switch((*T)->bf) { case LH: LeftBalance(T); if((*T)->lchild->bf == EH) //画图,不多说 { *shorter = 0; } else { *shorter = 1; } break; case EH: (*T)->bf = LH; *shorter = 0; break; case RH: (*T)->bf = EH; *shorter = 1; break; } } } return 0; }
测试代码主函数:
int _tmain(int argc, _TCHAR* argv[]) { int a[] = {3,2,1,4,5,6,7,10,9,8}; BiTree T = NULL; int taller; for (int i=0;i<10;++i) { InsertAVL(&T,a[i],&taller); } PrintBTree(T); int shorter; DeleteAVL(&T,10,&shorter); PrintBTree(T); printf("\n"); return 0;
打印结果:
相关文章推荐
- 洛谷1108 低价购买
- TFS中删除文件和重命名文件
- 个人--与老板对话1
- TFS中怪异的版本管理问题
- 在caffe中增加和convolution相同的层
- 单实例Singleton设计模式浅析
- 一位ACMer过来人的心得
- 常见排序的java代码
- QMetaObject感觉跟Delphi的类之类有一拼,好好学一下
- 一道蓝桥比赛的训练打印题【构造+不断的构造+构造规律】
- UVa 10044 - Erdos Numbers
- 8-20
- PHP中正则表达式学习及应用(二)
- Web前端从入门到精通-9 css简介——盒模型1
- 个人--规划--一天
- 【LeetCode】263. Ugly Number
- 709 异形卵
- JQuery 效果之隐藏与显示、淡入淡出、滑动、回调
- POJ 1704 Georgia and Bob(nim变型)
- poj 1847 Tram