前言
对于一个二叉排序树而言
- 它们的结构都是根据了二叉树的特性从最左子树开始在回到该结点上继续往右结点走
- 通过该方式进行递归操作,并且该二叉排序树的结构也是从小到大依次显示
- 那么我们假设a[10]={ 3,2,1,4,5,6,7,10,9,8 };我们需要查找改列表中的某一个结点的值
那么我们通过二叉排序树的展示,会展示成如图:
可以发现,如果我们想通过二叉排序树这个深度为8的树来查找某个数
我们需要走到最后,这是最坏的打算
但是如果我们能将该树改变为平衡二叉树,如图所示:
相比之下我们这个深度为4的平衡二叉树在通过查询值时是效率很平均且稳定的,
那么通过上面的问题我们该怎么将这个二叉排序树的变成平衡二叉树是我们现在的问题了。
一、平衡二叉树实现原理
对于平衡二叉树的特性而言,我们在上面也有举例到
- 在二叉排序树中,按照它的从小到大的结构所排序下来的结点,
- 当它的平衡因子的绝对值大于1的结点的根的子树,我们称为最小不平衡子树。
而我们该如何去计算该二叉排序树的平衡因子(平衡因子 = 左子树 - 右子树的值)呢?
我们可以结合上面那个最坏打算的二叉排序树的生成方式进行逐一分析。
如图:
可以看到当我们单步调试二叉排序树的时候到值1的时候值3的平衡因子为2>1
- (左子树有值2、值1所以是2-右子树0所以等于2)
那么此时并不符合我们的平衡二叉树的规则
那么我们需要通过某种形式去调换,使其符合我们的平衡二叉树的规则
而图2就是由图1通过顺时针而得到出来的,在这里我们称为右旋
- 此时我们所有的平衡因子<2那么又恢复了正常
之后我们的值4继续插入,因为值4>值3,所以根据二叉排序树的特性是值3的右子树
- 此时我们查看图3发现所有的平衡因子<2,
那么也没出现什么问题之后继续插入下一个值5,因为值5>值4,所以值5会到值4的右子树上。
如图所示:
可以发现图4中当插入值5的时候,根节点、值3的平衡因子的绝对值=2
- 所以是不符合平衡条件的所以此时我们通过逆时针的方式去旋转值3下面的所有结点
- 在这里我们称为左旋,得到的结果即为图5所示了,且此时每个结点的平衡因子都<2,恢复平衡了
剩下就不多去演示了,整体是一个递归的过程
重要:在过程中反复的把不平衡的结点通过左、右旋的方式将其调整回平衡二叉树。
要想深刻了解平衡二叉树的实现原理请自行参考程杰著作的《大话数据结构》。
二、平衡二叉树实现算法
在类型上我们会通过二叉排序树的基础上添加一个bf,用于存储平衡因子,而定义了一个Status则是判断当前状态的代码。
代码如下:
typedef int Status;
typedef struct BiTNode
{
int data;
int bf;
struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;
为了方便理解,我们前面的提到的左旋和右旋的方式,在c语言的代码中该如何实现呢?下面我们就来将代码展示出来。
代码如下:
void R_Rotate(BiTree* P)
{
BiTree L;
L = (*P)->lchild;
(*P)->lchild = L->rchild;
L->rchild = (*P);
*P = L;
}
void L_Rotate(BiTree* P)
{
BiTree R;
R = (*P)->rchild;
(*P)->rchild = R->lchild;
R->lchild = (*P);
*P = R;
}
我们可以看到左旋和右旋的代码很类似,所以我们通过了解了右旋的代码的含义自然就可以反推出左旋的含义了。
此函数的意思是:
- 当某一个结点P处于一个不平衡状态的时候,且平衡因子为正数需要右旋(顺时针),并将它的左孩子结点定义为L。
- 将L的右子树变成P的左子树,再将P改成L的右子树,最后将L替换P成功根结点。
如果文字还不了解我们可以将上述图1、图2的图片进行推导。
如图:
那么左旋的操作就是一个逆时针的方向转换,代码也是类似这里就不再演示了。
- 但是在这里我们还有另一种情况,就是当有两个或以上的平衡因子的绝对值>=2的时候,它们的平衡因子的值可能为负也可能为正,那么这种情况我们就要考虑到双旋操作了,
- 我们假设以树根为顶点的左右子树都需要考虑,且它们是相对的,所以我们可以通过了解左平衡旋转处理也就继而反推出右平衡旋转处理了。
现在我们来看左平衡旋转处理的代码函数:
#define LH +1
#define EH 0
#define RH -1
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);
L_Rotate(T);
}
}
void LeftBalance(BiTree* T)
{
BiTree L, Lr;
L = (*T)->lchild;
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);
R_Rotate(T);
}
}
如图:
首先通过代码可以看到,我们定义了三个常数变量,分别代码1、0、-1。
然后呢因为是左平衡旋转处理我们需要获取当前图片所示的P结点(也就是我之前假设的根节点的顶点)的左子树,
这里要注意重点:
- 当我们这个函数可以执行的时候已经确认了当前子树是不平衡的状态;
- 且结点N的值(即新插入结点) < 结点P的值(即上述图片根结点P)和结点P->左子树高于结点P->右子树需要调整;
- 并且你还要记住此时进来的结点P才是不平衡状态,且通过该P结点->左子树的平衡因子来进行分支判断。
- 如果等于1了则执行一次右旋操作,即上图右旋推导的并且把它们的BF值(平衡因子)都改为0操作;
- 反之如果等于-1了则像上图一样进行双旋操作;
- 不过在那之前还要通过获取图2上L的右子树LR(贴合代码上的Lr);
- 然后再判断一下Lr的平衡因子的值并再次进行分支判断;
- 修改根节点P(即代码中的根节点T)根据Lr的BF值进行修改它们的BF值;
- 最后在将Lr的BF值赋为0,进行双旋操作这里先左旋后右旋;
- 那么右平衡旋转处理函数则取反。
三、全部代码
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100
typedef int Status;
typedef struct BiTNode
{
int data;
int bf;
struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;
void R_Rotate(BiTree* P)
{
BiTree L;
L = (*P)->lchild;
(*P)->lchild = L->rchild;
L->rchild = (*P);
*P = L;
}
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
void LeftBalance(BiTree* T)
{
BiTree L, Lr;
L = (*T)->lchild;
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);
R_Rotate(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);
L_Rotate(T);
}
}
Status InsertAVL(BiTree* T, int e, Status* taller)
{
if (!*T)
{
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e; (*T)->lchild = (*T)->rchild = NULL; (*T)->bf = EH;
*taller = TRUE;
}
else
{
if (e == (*T)->data)
{
*taller = FALSE; return FALSE;
}
if (e < (*T)->data)
{
if (!InsertAVL(&(*T)->lchild, e, taller))
return FALSE;
if (*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
{
if (!InsertAVL(&(*T)->rchild, e, taller))
return FALSE;
if (*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;
}
int main(void)
{
int i;
int a[10] = { 3,2,1,4,5,6,7,10,9,8 };
BiTree T = NULL;
Status taller;
for (i = 0; i < 10; i++)
{
InsertAVL(&T, a[i], &taller);
}
printf("本样例建议断点跟踪查看平衡二叉树结构");
return 0;
}
到此这篇关于如何使用C语言实现平衡二叉树 的文章就介绍到这了,更多相关C语言实现平衡二叉树内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!