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

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

 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. Sum It Up---poj1564(dfs)

    题目链接:http://poj.org/problem?id=1564 给出m个数,求出和为n的组合方式:并按从大到小的顺序输出: 简单的dfs但是看了代码才会: #include <cstdi ...

  2. WPF自定义代码启动应用程序启动方式

    删除app.xaml 添加App.cs类 第一种方式: class App    {        [STAThread]        static void Main()        {     ...

  3. 关于Controller层返回JSON字符串

    /** * 导入jackson包. * @param pn * @return */ @RequestMapping("/emps") @ResponseBody public M ...

  4. Myeclipse文档注释如何提炼(导出)成自己的API帮助文档?

    第一步: 源码注释规范,一定要用/** 两个*这一特殊的注释.注释上可以添加@author等特殊说明,下图是部分 javadoc 标记 信息,可以根据需要选用.   第二步: 确保整个工程的项目都添加 ...

  5. JQuery UI中的Tabs与base元素摩擦的BUG

    JQuery UI中的Tabs与base元素冲突的BUG 以前一直使用jquery-ui-1.8,最近打算试一下目前最新的版本1.11.但对于Tabs,页面是乱的,怎么也不正常.折腾了好几个小时,最后 ...

  6. 自动化测试中级篇——LazyAndroid UI自动化测试框架使用指南

    原文地址https://blog.csdn.net/iamhuanggua/article/details/53104345 简介   一直以来,安卓UI自动化测试都存在以下两个障碍,一是测试工具Mo ...

  7. Cannot find entry file index.android.js in any of the roots:[ Android ]

    Changed the version of react project to a lower one from here npm install -g rninit rninit init [Pro ...

  8. uva11090 Bellman-Ford 运用

    给定一一个n个点m条边的加权有向图, 平均值最小的回路. 二分答案,对于每个二分的mid 做一次Bellman-Fprd , 假设有k条边组成的回路. 回路上各条边的权值为  w1 , w2 ..wk ...

  9. Arthur and Brackets

    n<605设计出n对夸号  给出n个条件每个条件为[l,r] 表示第i对夸号右夸号离左夸号的距离,然后夸号的右夸号出现的顺序必须按照给的顺序 出现, 那么如果存在就输出否则输出impossilb ...

  10. Qt信号和槽连接方式的选择

    看了下Qt的帮助文档,发现connect函数最后还有一个缺省参数. connect函数原型是这样的: QMetaObject::Connection QObject::connect(const QO ...