看完了第一篇博客,相信大家对于平衡二叉树的插入调整以及删除调整已经有了一定的了解,下面,我们开始介绍代码部分。

  首先,再次提一下使用的结构定义

 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树之代码篇的更多相关文章

  1. 平衡二叉树,AVL树之图解篇

    学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...

  2. 二叉查找树(BST)、平衡二叉树(AVL树)(只有插入说明)

    二叉查找树(BST).平衡二叉树(AVL树)(只有插入说明) 二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点, ...

  3. Java 树结构实际应用 四(平衡二叉树/AVL树)

    平衡二叉树(AVL 树) 1 看一个案例(说明二叉排序树可能的问题) 给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在.  左边 BST 存在的问题分析: ...

  4. 图解:平衡二叉树,AVL树

    学习过了二叉查找树,想必大家有遇到一个问题.例如,将一个数组{1,2,3,4}依次插入树的时候,形成了图1的情况.有建立树与没建立树对于数据的增删查改已经没有了任何帮助,反而增添了维护的成本.而只有建 ...

  5. 二叉查找树(BST)、平衡二叉树(AVL树)

    二叉查找树(BST) 特殊的二叉树,又称为排序二叉树.二叉搜索树.二叉排序树. 二叉查找树实际上是数据域有序的二叉树,即对树上的每个结点,都满足其左子树上所有结点的数据域均小于或等于根结点的数据域,右 ...

  6. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  7. 大话数据结构—平衡二叉树(AVL树)

    平衡二叉树(Self-Balancing Binary Search Tree/Height-Balanced Binary Search Tree),是一种二叉排序树,当中每个节点的左子树和右子树的 ...

  8. 平衡二叉树-AVL树(LL、RR、LR、RL旋转)

    平衡二叉树的定义: 任意的左右子树高度差的绝对值不超过1,将这样的二叉树称为平衡二叉树,二叉平衡树前提是一个二叉排序树. 平衡二叉树的插入: 二叉平衡树在插入或删除一个结点时,先检查该操作是否导致了树 ...

  9. 数据结构之平衡二叉树(AVL树)

    平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...

随机推荐

  1. day13(JSTL和自定义标签&MVC模型&javaweb三层框架)

    day13 JSTL标签库(重点) 自定义标签(理解) MVC设计模式(重点中的重点) Java三层框架(重点中的重点) JSTL标签库   1 什么是JSTL JSTL是apache对EL表达式的扩 ...

  2. 301-React Ext-React创建组件的三种方式及其区别

    一.概述 React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归:具体的三种方式: 函数式定义的无状态组件 es5原生方式React.createClass定义的组件 es6形 ...

  3. ZOHO 免费小型企业邮箱和个人邮箱

    Zoho Mail 提供免费小型企业邮箱注册.精简版只能添加一个域到您的机构帐号,最多允许10用户.如果您想添加多个域,您可以升级到标准版.10用户免费,5 GB /每用户,5 GB (共享). 除了 ...

  4. NodeJS学习笔记四

    Generator简介 基本概念 Generator函数有多种理解角度.从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态. 执行Generator函数会返回一个遍历 ...

  5. 散列表Java实现

    package 散列表; import java.util.Scanner; public class HashSearch { public static int data[] = {69,65,9 ...

  6. CCF地铁修建

    问题描述 A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁. 地铁由很多段隧道组成,每段隧道连接两个交通枢纽.经过勘探,有m段隧道作为候选,两个交通 ...

  7. 初识CGI

    CGI Web 服务器只能生成静态内容,而用户请求动态内容时,Web服务器只能借助一些应用程序来实现.CGI时一套标准,它规定了Web服务器和应用程序之间的交互方式. 静态内容与动态内容 要想理解什么 ...

  8. CentOS禁用笔记本touchpad

    自己在家笔记本装来个双系统玩玩,发现触摸板很烦人,禁用! 1.安装一个小神器 yum install xorg-x11-apps 2.查看你到输入硬件对应的id,方便禁用命令 [root@huangz ...

  9. python基础学习十 logging模块详细使用【转载】

    很多程序都有记录日志的需求,并且日志中包含的信息既有正常的程序访问日志,还可能有错误.警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,主要用于输出 ...

  10. 从e.getMessage()为null看Java异常机制

    问题:自定义异常触发了,但是自定义的提示信息RuntimeException却没有带过来. throw new RuntimeException("不允许插入报价主项和报价子项同时重复的记录 ...