平衡二叉树,AVL树之代码篇
看完了第一篇博客,相信大家对于平衡二叉树的插入调整以及删除调整已经有了一定的了解,下面,我们开始介绍代码部分。
首先,再次提一下使用的结构定义
typedef char KeyType; //关键字
typedef struct MyRcdType //记录
{
KeyType key;
}RcdType,*RcdArr;
typedef enum MyBFStatus //为了方便平衡因子的赋值,这里进行枚举
{ //RH,EH,LH分别表示右子树较高,左右子树等高,左子树较高
RH,EH,LH
}BFStatus;
typedef struct MyBBSTNode //树结点类型定义
{
RcdType data; //数据成员
BFStatus bf; //平衡因子
struct MyBBSTNode *lchild,*rchild; //左右分支
}BBSTNode,*BBSTree;
结构定义
1. 旋转
旋转是平衡二叉树的基础。所以我们首先介绍。先看具体代码。RRotate的作用就是以*T为根结点的二叉树向右旋转,LRotate就是向左旋转。
参数说明:*T为待旋转子树的根结点。
BBSTree RRotate(BBSTree *T)
{
BBSTree lchild;
lchild = (*T)->lchild;
(*T)->lchild = lchild->rchild;
lchild->rchild = (*T);
(*T) = lchild;
}
void LRotate(BBSTree *T)
{
BBSTree rchild;
rchild = (*T)->rchild;
(*T)->rchild = rchild->lchild;
rchild->lchild = *T;
*T = rchild;
}
左右旋转
2. 失衡调整
在前一篇博客我们已经知道,失衡的情况主要包括了两种,左子树过高与右子树过高。我们按其对代码进行划分,首先介绍左子树过高的情况,我们称为左平衡处理。
在这里,不得不再提一下,我们将左失衡,即做左子树过高的情况分为了三种,插入新的结点时,可能出现的情况为LL与LR,删除的时候可能出现的情况为LL,LR,LE。假如不明白为何LE只出现在删除的时候出现,请查看上一篇博客的插入调整与删除调整部分的内容。
函数先行条件:*T为根的树为不平衡子树。
参数说明:*T为不平衡子树的根结点
函数大致说明:首先根据LL,LR,LE对不平衡树进行划分,设定旋转后的最终平衡因子,再对*T进行相应旋转。例如,LL型的不平衡树,最后为*T的左孩子作为根结点,首先设定为最终的值,*T的左孩子的平衡因子为EH,初始根的平衡因子也为EH。
void LeftBalance(BBSTree *T)
{
BBSTree lchild,rchild;
lchild = (*T)->lchild;
switch (lchild->bf)
{
case EH:
(*T)->bf = lchild->bf = LH;
lchild->bf = RH;
RRotate(T);
break;
case LH:
(*T)->bf = lchild->bf = EH;
RRotate(T);
break;
case RH:
rchild = lchild->rchild;
switch (rchild->bf)
{
case LH:
(*T)->bf = RH; lchild->bf = EH; break;
case RH:
(*T)->bf = EH; lchild->bf = LH; break;
case EH:
(*T)->bf = EH; lchild->bf = EH;
}
rchild->bf = EH;
LRotate(&((*T)->lchild));
RRotate(T);
break;
}
}
左平衡调整
void RightBalance(BBSTree *T)
{
BBSTree rchild,lchild;
rchild = (*T)->rchild;
switch (rchild->bf)
{
case RH:
(*T)->bf = rchild->bf = EH;
LRotate(T);
break;
case EH:
(*T)->bf = RH;
rchild->bf = LH;
LRotate(T);
break;
case LH:
lchild = rchild->lchild;
switch (lchild->bf)
{
case LH:
rchild->bf = RH; (*T)->bf = EH;
break;
case RH:
rchild->bf = EH; (*T)->bf = LH;
break;
case EH:
rchild->bf = EH; (*T)->bf = EH;
break;
}
lchild->bf = EH;
RRotate(&((*T)->rchild));
LRotate(T);
break;
}
}
右平衡调整
3. 插入新的结点
说明:在插入新的结点的时候,我们使用一个taller的变量来记录树的高度是否变化。默认认为树的高度是有增加的。我们在插入新的结点后,首先判断树的高度是否增加了,假如树的高度没有变化,不必进行如何操作。当树的高度增加时,我们就考虑是否需要对树的进行平衡调整。假如原本根的平衡因子为LH,而插入点又在左子树上,并且子树的高度变高了的时候,我们就要进行左平衡处理。相对的,假如原本根的平衡因子为RH,而插入点又在右子树上,并且子树的高度变高了的时候,我们就要进行右平衡处理。而我们又知道,只需要对最小失衡树进行平衡调整,所以调整后要将taller置为FALSE
参数说明:*T,待插入的平衡二叉树
e,带插入的新的结点的值
taller,*T的子树的高度是否变高的标志。
Status InsertAVL(BBSTree *T,RcdType e,Status *taller)
{
if(!(*T)) //新建一个节点
return CreatBBSTNode(T,e);
else if(e.key == (*T)->data.key)
{
*taller = FALSE;
return TRUE;
}
if(e.key < (*T)->data.key) //插入到左子树
{
Status sign = InsertAVL(&(*T)->lchild,e,taller);
if(FALSE == sign || OVERFLOW == sign)
return FALSE;
if(TRUE == *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 //插入到了右子树
{
Status sign = InsertAVL(&(*T)->rchild,e,taller);
if(FALSE == sign || OVERFLOW == sign)
return FALSE;
if(TRUE == *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;
}
插入新的结点
4. 删除
说明:平衡二叉树也是一棵二叉查找树,所以其删除操作与二叉查找树是一致的。只是我们需要进行平衡处理。我们使用bfChild记录待删除结点的的子树的根结点的原平衡因子。新的平衡因子由子树的根结点的bf成员进行记录。当待删除的结点处于当前节点的左分支上时,删除结点后,我们调用DelLeftCase设定树的平衡因子以及对树进行调整。当待删除的结点处于当前节点的右分支上时,删除结点后,我们调用DelRightCase设定树的平衡因子以及对树进行调整。而我们需要对结点的平衡因子进行重新设定,只有在子树的高度有所降低时进行。而子树的高度降低,对应着Del***Case函数中的子树是否变为NULL或者子树的平衡因子从LH或者RH变为EH。
参数说明:*T为待进行调整的子树的根结点
bfChild为*T的左孩子在删除结点前的平衡因子
//参数说明:*T为待进行调整的子树的根结点
//bfChild为*T的右孩子在删除结点前的平衡因子
void DelLeftCase(BBSTree *T,int bfChild)
{
//当bf为-1或1变为0,或者孩子为空时说明子树高降低
if((!(*T)->lchild) || (EH != bfChild && EH == (*T)->lchild->bf))
{
switch ((*T)->bf)//左子树树高降低
{
case EH:
(*T)->bf = RH;
break;
case LH:
(*T)->bf = EH;
break;
case RH: //原本右子树比较高
RightBalance(T);
break;
}
}
} void DelRightCase(BBSTree *T,int bfChild)
{
//当bf为LH或RH变为EH,或者孩子为空时说明子树高降低
if((!(*T)->rchild) || (EH != bfChild && EH == (*T)->rchild->bf))
{
switch ((*T)->bf)
{
case EH:
(*T)->bf = LH;
break;
case RH:
(*T)->bf = EH;
break;
case LH: //原本左子树比较高
LeftBalance(T);
break;
}
}
}
BBSTree DeleteNode(BBSTree *T,KeyType key)
{
int bfChild;
if(*T)
{
if((*T)->data.key > key)
{
bfChild = (*T)->lchild->bf;
(*T)->lchild = DeleteNode(&(*T)->lchild,key);
DelLeftCase(T,bfChild);
}
else if((*T)->data.key < key)
{
bfChild = (*T)->rchild->bf;
(*T)->rchild = DeleteNode(&(*T)->rchild,key);
DelRightCase(T,bfChild);
}
else//当前节点就是要删除的节点
{
if((*T)->lchild) //*T不是叶子结点并且具有直接前驱
{
BBSTree farRight = GofarRight((*T)->lchild);
(*T)->data = farRight->data;
//可以确定,删除的节点为当前节点的左子树的某一个节点
(*T)->lchild = DeleteNode(&(*T)->lchild,farRight->data.key);
DelLeftCase(T,bfChild);
}
else if((*T)->rchild) //*T不是叶子结点并且具有直接后驱
{
BBSTree farLeft = GofarLeft((*T)->rchild);
(*T)->data = farLeft->data;
(*T)->rchild = DeleteNode(&(*T)->rchild,farLeft->data.key);
DelRightCase(T,bfChild);
}
else //*T是叶子结点
{
free(*T);
*T = NULL;
}
}
}
return (*T);//包含了返回NULL与正常的当前节点
}
删除
假如您看了图解篇以及代码篇,或许对AVL有了一定的了解。不过还是建议将所有的代码自己实现一次。下面附上完整测试代码的下载链接:http://yunpan.cn/cwUVwIYbPwsC8 访问密码 4302。
测试代码大概解释:使用#表示空结点。代码先自动生成一棵平衡二叉树,首先删除一个结点,用户输入Y就会再次删除平衡二叉树的一个结点。每次删除完了会将树进行输出。输入的格式为A【B,C】的格式,A代表根结点,B为A的左孩子,C为A的右孩子。输入其他字符可以查看最终结果。
PS:假如代码无法编译,请检查随机函数在你的编译器是否可用。
欢迎各位朋友批评改正,谢谢。
平衡二叉树,AVL树之代码篇的更多相关文章
- 平衡二叉树,AVL树之图解篇
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
- 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)
二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...
- Java 树结构实际应用 四(平衡二叉树/AVL树)
平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在. 左边 BST 存在的问题分析: ...
- 图解:平衡二叉树,AVL树
学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...
- 二叉查找树(BST)、平衡二叉树(AVL树)
二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右 ...
- 【数据结构】平衡二叉树—AVL树
(百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 大话数据结构—平衡二叉树(AVL树)
平衡二叉树(Self-Balancing Binary Search Tree/Height-Balanced Binary Search Tree),是一种二叉排序树,当中每个节点的左子树和右子树的 ...
- 平衡二叉树-AVL树(LL、RR、LR、RL旋转)
平衡二叉树的定义: 任意的左右子树高度差的绝对值不超过1,将这样的二叉树称为平衡二叉树,二叉平衡树前提是一个二叉排序树. 平衡二叉树的插入: 二叉平衡树在插入或删除一个结点时,先检查该操作是否导致了树 ...
- 数据结构之平衡二叉树(AVL树)
平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...
随机推荐
- 大话https演化过程(对称加密、非对称加密、公钥、私钥、数字签名、数字证书)
大话https演化过程(包括概念:对称加密.非对称加密.公钥.私钥.数字签名.数字证书.https访问全过程) 在网络上发送数据是非常不安全的,非常容易被劫持或是被篡改,所以每次定向发送数据你都可 ...
- Linux常用命令【总结】
Linux命令中文版详解:https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/index.html Linux系统,我用过的有centos和 ...
- mysql 表的增删改查 修改表结构
四.修改表结构 语法: . 修改表名 ALTER TABLE 表名 RENAME 新表名; . 增加字段 ALTER TABLE 表名 ADD 字段名 数据类型 [完整性约束条件…], ADD 字段名 ...
- Java中内部类揭秘(一):外部类与非静态内部类的”相互可见性“
声明:本博客为原创博客.未经同意,不得转载.原文链接为 http://blog.csdn.net/bettarwang/article/details/27012421. ...
- ajax请求,html调用js
1:html中调用js中的函数,js使用ajax请求向后台请求,返回数据. <!DOCTYPE html> <html lang="en"> <hea ...
- HDU1556:Color the ball(简单的线段树区域更新)
http://acm.hdu.edu.cn/showproblem.php?pid=1556 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定 ...
- 11月26号host
127.0.0.1 localhost255.255.255.255 broadcasthost::1 localhostfe80::1%lo0 localhost # Google start216 ...
- HttpClient-RestTemplate-Feign
如何通过Java发送HTTP请求,通俗点讲,如何通过Java(模拟浏览器)发送HTTP请求. Java有原生的API可用于发送HTTP请求,即java.net.URL.java.net.URLConn ...
- 【转载】Java动态代理之JDK实现和CGlib实现(简单易懂)
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6542259.html 一:代理模式(静态代理) 代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是 ...
- redis未授权访问漏洞总结
Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. 漏洞介绍: Redis 默认情况下,会绑定在 0.0.0.0 ...