平衡二叉树的实现代码加详细注释

当然了,说是详细的注释,但其实只是自己的一些想法。

下面的代码只是简单的平衡二叉树的建立,还没有增添删除功能,我会在接下来的时间补完代码再进行编辑的。

如果有误,还请各位多多指点。万分感谢。

以下是代码部分:

#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的值彻底的改变。
所以指针也是一样的,如果我们想要改变指针的指向,就必须将这个指针的地址作为参数送进函数里面进行改变,这样做才能改变指针的指向。
指针变量也是变量,只不过它的值是地址而已。
因此我们才会使用二重指针。

分:

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/huangguangyourena/article/details/77721270
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞