数据结构与算法——平衡二叉树(AVL Tree)

数据结构与算法——平衡二叉树(AVL Tree)

构建一个二叉排序树时,如果构建序列是完全有序的,则会出现这样的情况:

 

显然这种情况会使得二叉搜索树退化成链表。当出现这样的情况,二叉排序树的查找也就退化成了线性查找,所以我们需要合理调整二叉排序树的形态,使得树上的每个结点都尽量有两个子结点,这样整个二叉树的高度就会大约在log(n) 左右,其中 n 为结点个数。

基本性质

​ AVL树也称为平衡二叉树,是一种自平衡的二叉排序树,本质上仍然是一颗二叉排序树,只是增加了“平衡”的要求,平衡是指,对AVL树中任何节点的两个子树的高度之差(称为平衡因子)的绝对值不超过 1 。能保证上面这一点,AVL树的高度就能始终保持在 O(logn)级别。

数据结构

typedef struct BiTNode 
{
	int data;
	int bf;//结点的平衡因子
	BiTNode *lchild;
	BiTNode *rchild;
}BiTNode,*BiTree;

右旋操作

当最小不平衡子树根结点的平衡因子BF是大于1时,就右旋:

/*
*右旋处理
*/
void R_Rotate(BiTree *p) 
{
	BiTree L;
	L = (*p)->lchild;
	(*p)->lchild = L->rchild;
	L->rchild = (*p);
	*p = L;//p指向新的根结点
}

左旋操作

当最小不平衡子树根结点的平衡因子BF是小于-1时就左旋

/*
*左旋
*/
void L_Rotate(BiTree *p) 
{
	BiTree R;
	R = (*p)->rchild;
	(*p)->rchild = R->lchild;
	R->lchild = (*p);
	*p = R;
}

左平衡旋转

/*
*左平衡旋转
*插入结点后最小不平衡子树的BF与它的子树的BF符号相反时:
*1. 需要对结点先进行一次旋转以使符号相同
*2. 符号相同后需要再反向旋转一次
*/
#define LH  +1
#define EH  0
#define RH  -1

void LeftBalance(BiTree *T) 
{
	BiTree L, Lr;
	L = (*T)->lchild;
	switch (L->bf)
	{//检查T的左子树的平衡度,并作相应平衡处理
		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. L->bf == LH

此时新结点插入在T的左孩子的左树上,因为L为T的lchild,说明此时与根结点的符号相同,执行右旋操作:

2. L->bf == RH

此时的结点与根节点的BF符号相反,此时需要做双旋处理:

右平衡旋转

右平衡旋转和上述的左旋转相似,实现如下:

void RightBalance(BiTree *T)
{
	BiTree R, Rl;
	R = (*T)->rchild;
	switch (R->bf)
	{//检查T的右子树的平衡度,并作相应平衡处理
	case RH://
		(*T)->bf = R->bf = EH;
		L_Rotate(T);
		break;

	case LH:
		Rl = R->rchild;
		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);
	}
}

插入操作

插入操作的思想:利用递归的方式查找插入点,并在插入成功之后返回,从插入结点的父结点开始,按递归的逆顺序遍历结点,去判断该结点的二叉树是否是平衡树,如果不是,进行调整。

bool InsertAVL(BiTree *T,int e,bool *taller) 
{
	if (*T == nullptr) 
	{
		*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 )//在T的左子树中插入 
		{
			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)->lchild, 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值