当然了,说是详细的注释,但其实只是自己的一些想法。
下面的代码只是简单的平衡二叉树的建立,还没有增添删除功能,我会在接下来的时间补完代码再进行编辑的。
如果有误,还请各位多多指点。万分感谢。
以下是代码部分:
#include<stdio.h>
#include<stdlib.h>
#define true 1
#define false 0
#define LH 1 //表示左子树高
#define RH -1 //表示右子树高
#define EH 0 //左右子树等高
typedef int Elemtype;
typedef int Status;
typedef struct BiNode{ //构建结构体
Elemtype data;
int bf; //平衡因子
struct BiNode *rchild;
struct BiNode *lchild;
}BiNode,*BiTree;
void L_Rotate(BiTree *T) //左旋
{
BiTree p;
p = (*T)->rchild;
(*T)->rchild = p->lchild;
p->lchild = *T;
*T = p;
}
void R_Rotate(BiTree *T) //右旋
{
BiTree p;
p = (*T)->lchild;
(*T)->lchild = p->rchild;
p->rchild = *T;
*T = p;
}
void RightBalance(BiTree *T) //调整左平衡
{
BiTree R, r1;
R = (*T)->rchild; //R是根T的右儿子
switch (R->bf)
{
case RH: 这里的RH表示R的平衡因子,如果是RH的话那么进行左旋,右儿子成为新的根
(*T)->bf = R->bf = EH;
L_Rotate(T);
break;
case LH:
r1 = R->lchild; //这里其实是相对比较难以理解的部分,当新节点插在根的右儿子的左儿子上的时候,是不能通过一次旋转
//使其平衡的,因此首先我们先讨论根的右儿子的左儿子的平衡因子
switch (r1->bf)
{
case LH: //试着想象并画出一棵这样的树,根的右儿子的左儿子这个结点有两个高度为h-2子树(注意这里我是说子树,而不是结点)
(*T)->bf = EH; //,根的右儿子也有高度为h-1的右子树,再画一个左子树在根的左边,高度为h-1。
R->bf = RH; //r1->bf = LH的意思是在这个结点的左子树上插入了一个结点,导致该结点的左子树的高度从h-2变成h-1
break;
case RH:
(*T)->bf = LH; //RH也是一样,插在右子树上,画图会更容易理解
R->bf = EH;
break;
}
r1->bf = EH; //经过两次旋转,根的右儿子的左儿子会成为新的根
R_Rotate(&((*T)->rchild)); //先以根的右儿子进行左旋
L_Rotate(T); //再以根进行右旋
break;
}
}
void LeftBalance(BiTree *T)
{
BiTree L, L1;
L = (*T)->lchild;
switch (L->bf)
{
case LH:(*T)->bf = L->bf = EH;
R_Rotate(T);
break;
case RH:
L1 = L->rchild;
switch (L1->bf)
{
case LH:(*T)->bf = RH;
L->bf = EH;
break;
case RH:L->bf = LH;
(*T)->bf = EH;
break;
}
L1->bf = EH;
L_Rotate(&((*T)->lchild));
R_Rotate(T);
break;
}
}
Status InsertAVLtree(BiTree *T,Elemtype data,int *taller)
{
if ((*T) == NULL) //若要插入的那个地方为空,则分配内存给新节点
{
*T = (BiTree)malloc(sizeof(BiNode));
(*T)->data = data;
(*T)->bf = 0;
(*T)->rchild = (*T)->lchild = NULL;
*taller = true; //taller的作用是判断树是否长高
}
else
{
if (data == (*T)->data)
{
*taller = false;
return false;
}
if (data < (*T)->data)
{
if (false == InsertAVLtree(&((*T)->lchild), data, taller)) //有递归的作用在里面
return false;
if (*taller) //taller == true说明树长高了,所以要判断是否有结点失衡。否则繁反之。
{
switch ((*T)->bf)
{
case LH:LeftBalance(T);
*taller = false; //因为调整平衡后整棵树平衡,因此taller = false,不需要往上递归
break;
case EH:(*T)->bf = LH;
*taller = true;
break;
case RH:(*T)->bf = EH;
*taller = false;
break;
default:
break;
}
}
else
{
if (false == InsertAVLtree(&((*T)->rchild), data, 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;
default:
break;
}
}
}
}
}
return true;
}
void PreOrderTraversal(BiTree T) //递归前序遍历
{
if (T != NULL)
{
printf("%5d", T->data);
PreOrderTraversal(T->lchild);
PreOrderTraversal(T->rchild);
}
}
int main()
{
BiTree T = NULL;
int i;
int a[6] = { 10,15,23,5,6,1 };
int taller = true;
for (i = 0; i < 6; i++)
InsertAVLtree(&T, a[i], &taller); //如果InsertAVLtree的参数是BiTree T而不是BiTree *T的话,是没有办法改变T的值的,所以用前序遍历是没有用的
PreOrderTraversal(T); //因为跟值传递一样,只有改变地址才可以改变指针的指向。如果不改,那么T将永远指向NULL
printf("\n");
system("pause");
return 0;
}
结束部分还有一个需要解释的部分,就是除了递归前序遍历以外的函数参数为什么都用了二重指针。
因为我们知道C语言是值传递的,比如讲int a的值传进某个函数并改变a的值,但这样做并不会改变函数外面的a的值,这个时候我们只有将a的地址传进函数里面,才能将a的值彻底的改变。
所以指针也是一样的,如果我们想要改变指针的指向,就必须将这个指针的地址作为参数送进函数里面进行改变,这样做才能改变指针的指向。
指针变量也是变量,只不过它的值是地址而已。
因此我们才会使用二重指针。
分: