平衡二叉排序树

一.概念
在这里插入图片描述
最小不平衡子树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树

二.实现原理
基本思想:在构建二叉排序树时,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
1.BF为负时向左转,为正向右转
2.转向之前要确保最小不平衡子树的根结点与它的子结点符号是相同的 同正负再调向

举个例子
对于一个给定的数组 [3, 2, 1, 4, 5, 6, 7, 10, 9, 8]
在这里插入图片描述
在这棵树中可以看到只有根节点 2 是不平衡的,且 BF = -2 < 0,所以需要将根节点 2 向左旋转,得到如下的结果
在这里插入图片描述
由于 3 小于 4 ,所以将 3 连接在 4 的左子树的最右端,得到如下图所示的结果
在这里插入图片描述
添加9
在这里插入图片描述
但是这个结点不可以像之前一样通过旋转 7 10 9 这棵子树,因为这个时候, 10 结点的 BF 符号与之前的符号均不相同,所以要首先将 9 10 旋转,之后再将不平衡子树进行旋转,如下图所示
在这里插入图片描述
添加8
在这里插入图片描述
明显结点 4 的 BF 为正,结点 6 的 BF 为正,但是结点 9 的 BF 为负,这个时候应该先将 8 7 9 10 进行旋转,使得 BF 值相等,然后再调整二叉排序树,如下图所示

在这里插入图片描述
三.不平衡因子的四种情况
1.单向右旋平衡处理:若由于结点 a 的左子树为根结点的左子树上插入结点,导致结点 a 的平衡因子由 1 增至 2,致使以 a 为根结点的子树失去平衡,则只需进行一次向右的顺时针旋转,如下图这种情况
在这里插入图片描述
2.单向左旋平衡处理:如果由于结点 a 的右子树为根结点的右子树上插入结点,导致结点 a 的平衡因子由 -1变为 -2,则以 a 为根结点的子树需要进行一次向左的逆时针旋转,如下图这种情况:
在这里插入图片描述
3.双向旋转(先左后右)平衡处理:如果由于结点 a 的左子树为根结点的右子树上插入结点,导致结点 a 平衡因子由 1 增至 2,致使以 a 为根结点的子树失去平衡,则需要进行两次旋转操作,如下图这种情况:
在这里插入图片描述
4.双向旋转(先右后左)平衡处理:如果由于结点 a 的右子树为根结点的左子树上插入结点,导致结点 a 平衡因子由 -1 变为 -2,致使以 a 为根结点的子树失去平衡,则需要进行两次旋转(先右旋后左旋)操作,如下图这种情况:
在这里插入图片描述
四.构建平衡二叉树的代码实现
1.结点结构

typedef struct BiTNode{
	int data;
	int BF;
	struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

2.左右旋操作

void L_Rotate(BiTree *p)
{
	BiTree L;
	L = (*p) -> rchild;
	(*p) -> rchild = L -> lchild;
	L -> lchild = (*p);
	*p = L;
}

void R_Rotate(BiTree *p)
{
	BiTree L;
	L = (*p) -> lchild;
	(*p) -> lchild = L -> rchild;
	L -> rchild = (*p);
	*p = L;
}

3.左右平衡旋转处理

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 L, Lr;
	L = (*T) -> rchild;
	switch(L -> BF)
	{
		case RH:
			{
				(*T) -> BF = L -> BF = EH;
				L_Rotate(T);
				break;
			}
		case LH:
			{
				Lr = L -> lchild;
				switch(Lr -> BF)
				{
					case LH:
						{
							(*T) -> BF = EH;
							L -> BF = RH;
							break;
						}
					case EH:
						{
							(*T) -> BF = L -> BF = EH;
							break;
						}
					case RH:
						{
							(*T) -> BF = EH;
							L -> BF = LH;
							break;
						}
				}
				Lr -> BF = EH;
				R_Rotate(&(*T) -> rchild);
				L_Rotate(T);
			}
	}
}

4.二叉树的主函数

int InsertAVL(BiTree *T, int e, int *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) //应继续在T的左子树中进行搜索 
		{
			if(! InsertAVL(&(*T) -> lchild, e, taller))
			{
				return FALSE;
			}
			if(*taller) //已经插入到T的左子树里且左子树"长高"
			{
				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;
} 
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值