数据结构------二叉平衡树(一)插入操作

二叉平衡树是一种特殊的二叉树,它能有效的控制树的高度,避免产生普通二叉搜索树的“退化”树形

一,基本定义

1、二叉平衡树又称AVL树,它或是一棵空二叉树,或者是具有以下性质的二叉树。

(1)其根的左右子树高度只差的绝对值不超过1

(2)其根的左右子树都是二叉平衡树

二叉树结点上的平衡因子定义为该结点左子树的高度减去右子树的高度,即取值为-1,0,1

下面的研究都是基于二叉平衡搜索树,在此都称为二叉平衡树。

2、二叉平衡树的存储

采用链式存储,比二叉搜索树多一个域来表示平衡因子。

3、二叉平衡树的结点定义

/* 二叉平衡树节点的定义 */
typedef struct _AVLNode{
	int data;
	int bf;/*平衡因子*/
	struct _AVLNode *lchild,*rchild;
}AVLNode,*AVLTree;
4、二叉平衡树的平衡旋转

一般情况下,一个新结点插入后会影响从根到新结点路径上所有结点的平衡因子。如果插入在某个节点的左子树上,那么该结点的平衡因子+1,否则-1。

如果插入之前,从根结点到新结点插入位置路径上所有结点的平衡因子均为0,那么插入后这个二叉树仍然是二叉平衡树,但高度+1。若这条路径上某个节点的平衡因子不为0(由s指向),但自它一下直至新结点的所有结点的平衡因子原先都是0,那么插入新节点后,以结点s为根的子树将是可能不平衡的最小子树。
情况一:

插入之前,从根结点到新结点q的插入位置的路径上,所以后结点的平衡因子均为0,那么插入后,只需将根结点的平衡因子改为1。树的高度+1.如图所示:


情况二:

若新结点q插在s较矮的子树上,则插入后需要修改s的平衡因子为0.如图所示:


情况三:

LL旋转--新结点q插入在结点s的左子树上,设r是s的左孩子且r的平衡因子bf为1。这里s的平衡因子1是插入前的值,而r的平衡因子是修正后的值。显然,插入q后,以s为根子树平衡性被破坏了。这种情况通过LL旋转来恢复。如图所示:


LR旋转---新结点q插入在结点s的左子树的右子树上,并且r的平衡因子为-1,这种情况下执行的操作成为LR旋转,如下图所示。


二、程序的实现

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* 二叉平衡树节点的定义 */
typedef struct _AVLNode{
	int data;
	int bf;/*平衡因子*/
	struct _AVLNode *lchild,*rchild;
}AVLNode,*AVLTree;
/*创建一个二叉搜索树节点,x为关键字值 */
AVLTree Create(int x)
{
	AVLTree avl=(AVLTree)malloc(sizeof(AVLNode));
	if(avl==NULL)
	{
		perror("创建失败");
		exit(1);
	}
	avl->data=x;
	avl->bf=0;
	avl->lchild=avl->rchild=NULL;
	return avl;
}
/*左旋转函数*/
void LRotation(AVLTree *avl,int *unbalance)
{
	AVLTree u,r=(*avl)->lchild;
	if(r->bf==1)//LL 旋转
	{
		(*avl)->lchild=r->rchild;
		r->rchild=*avl;
		(*avl)->bf=0;
		(*avl)=r;/*(*avl)指向新子树的根*/
	}
	else/*LR旋转*/
	{
		u=r->rchild;
		r->rchild=u->lchild;
		u->lchild=r;
		(*avl)->lchild=u->rchild;
		u->rchild=*avl;
		switch(u->bf)
		{
			case 1: 
				(*avl)->bf=-1;
				r->bf=0;
				break;
			case 0:
				(*avl)->bf=r->bf=0;
				break;
			case -1:
				(*avl)->bf=0;
				r->bf=1;
		}
		*avl=u;/*(*avl)指向新子树的根*/
	}
	(*avl)->bf=0;
	*unbalance=0;
}
void RRotation(AVLTree *avl,int *unbalance)
{
	AVLTree u,r=(*avl)->rchild;
	if(r->bf==-1)//RR 旋转
	{
		(*avl)->rchild=r->lchild;
		r->lchild=(*avl);
		(*avl)->bf=0;
		(*avl)=r;/*(*avl)指向新子树的根*/
	}
	else
	{
		u=r->lchild;
		r->lchild=u->rchild;
		u->rchild=r;
		(*avl)->rchild=u->lchild;
		u->lchild=(*avl);
		switch(u->bf)
		{
			case -1: 
				(*avl)->bf=-1;
				r->bf=0;
				break;
			case 0:
				(*avl)->bf=r->bf=0;
				break;
			case 1:
				(*avl)->bf=0;
				r->bf=1;
		}
			(*avl)=u;/*(*avl)指向新子树的根*/
	}
	(*avl)->bf=0;
	*unbalance=0;
}
void Insert(AVLTree* avl,int x,int *unbalance)
{
	if(*avl==NULL)
	{/*生成新结点再插入*/
		*avl=Create(x);
		*unbalance=1;
	}
	else if(x<((*avl)->data))/*新结点插入左子树*/
	{
		Insert(&((*avl)->lchild),x,unbalance);
		if(*unbalance)
		{
			switch((*avl)->bf)
			{
				case -1:
					(*avl)->bf=0;
					*unbalance=0;
					break;
				case 0:
					(*avl)->bf=1;
					break;
				case 1:
					LRotation(avl,unbalance);
			}
		}
	}
		else if(x==(*avl)->data)/*元素存在直接返回*/
		{
			return ;
		}
		else/*新结点插入右子树*/
		{
			Insert(&((*avl)->rchild),x,unbalance);
			if(*unbalance)
			{
				switch((*avl)->bf)
				{
				case 1:(*avl)->bf=0;
					*unbalance=0;break;
					break;
				case 0:
					(*avl)->bf=-1;
					break;
				case -1:
					RRotation(avl,unbalance);
				}
			}
		}
}
/*中序遍历 */
void  InOrder(AVLTree bst)
{
	if(bst==NULL)
		return ;
	else
	{
		InOrder(bst->lchild);
		printf("%d ",bst->data);
		InOrder(bst->rchild);
		
	}
}
/*前序遍历 */
void  PreOrder(AVLTree bst)
{
	if(bst==NULL)
		return ;
	else
	{
		printf("%d ",bst->data);
		PreOrder(bst->lchild);
		PreOrder(bst->rchild);
		
	}
}
int main()
{
	AVLTree avl=NULL;
	int balance;
	Insert(&avl,45,&balance);
	Insert(&avl,28,&balance);
    Insert(&avl,15,&balance);
	Insert(&avl,12,&balance);
	Insert(&avl,14,&balance);	
	Insert(&avl,13,&balance);
	Insert(&avl,11,&balance);
	Insert(&avl,47,&balance);
	Insert(&avl,57,&balance);
	Insert(&avl,46,&balance);
	InOrder(avl);
	printf("\n");
	PreOrder(avl);
}
输出结果为:




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值