平衡二叉树(AVL)

代码参考:http://blog.youkuaiyun.com/wypblog/article/details/8119616

平衡二叉树(Balanced binary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskii and Landis)1962年首先提出的,所以又称为AVL树。

定义:平衡二叉树或为空树,或为如下性质的二叉排序树:

  1)左右子树深度之差的绝对值不超过1;

  2)左右子树仍然为平衡二叉树.

      平衡因子BF=左子树深度-右子树深度.

平衡二叉树每个结点的平衡因子只能是10-1。若其绝对值超过1,则该二叉排序树就是不平衡的。

如图所示为平衡树和非平衡树示意图:

二、平衡二叉树算法思想

若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。

        失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树。假设用A表示失去平衡的最小子树的根结点,则调整该子树的操作可归纳为下列四种情况。

 1LL型平衡旋转法

由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。即将A的左孩子B右上旋转代替A作为根结点,A右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。

2RR型平衡旋转法

由于在A的右孩子C的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C左上旋转代替A作为根结点,A左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。

3LR型平衡旋转法

由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理

      如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。

4RL型平衡旋转法 

由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。

 如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。

平衡化靠的是旋转。参与旋转的是3个节点(其中一个可能是外部节点NULL),旋转就是把这3个节点转个位置。注意的是,左旋的时候p->right一定不为空,右旋的时候p->left一定不为空,这是显而易见的。

如果从空树开始建立,并时刻保持平衡,那么不平衡只会发生在插入删除操作上,而不平衡的标志就是出现bf == 2或者 bf == -2的节点。

#include <stdio.h>
#include <stdlib.h>
/************************************************************************/
/*                    平衡二叉树---AVL                                  */
/************************************************************************/
#define LH +1
#define EH  0
#define RH -1
typedef int ElemType;
typedef struct BSTNode{
	ElemType data;
	int bf;//balance flag
	struct BSTNode *lchild,*rchild;
}*PBSTree;

void R_Rotate(PBSTree* p)
{
	PBSTree lc = (*p)->lchild;
	(*p)->lchild = lc->rchild;
	lc->rchild = *p;
	*p = lc;
}

void L_Rotate(PBSTree* p)
{
	PBSTree rc = (*p)->rchild;
	(*p)->rchild = rc->lchild;
	rc->lchild = *p;
	*p = rc;
}

void LeftBalance(PBSTree* T)
{
	PBSTree lc,rd;
	lc = (*T)->lchild;
	switch (lc->bf)
	{
	case LH:
		(*T)->bf = lc->bf = EH;
		R_Rotate(T);
		break;
	case RH:
		rd = lc->rchild;
		switch(rd->bf)
		{
		case LH:
			(*T)->bf = RH;
			lc->bf = EH;
			break;
		case EH:
			(*T)->bf = lc->bf = EH;
			break;
		case RH:
			(*T)->bf = EH;
			lc->bf = LH;
			break;
		}
		rd->bf = EH;
		L_Rotate(&(*T)->lchild);
		R_Rotate(T);
		break;
	}
}

void RightBalance(PBSTree* T)
{
	PBSTree lc,rd;
	lc= (*T)->rchild;
	switch (lc->bf)
	{
	case RH:
		(*T)->bf = lc->bf = EH;
		L_Rotate(T);
		break;
	case LH:
		rd = lc->lchild;
		switch(rd->bf)
		{
		case LH:
			(*T)->bf = EH;
			lc->bf = RH;
			break;
		case EH:
			(*T)->bf = lc->bf = EH;
			break;
		case RH:
			(*T)->bf = EH;
			lc->bf = LH;
			break;
		}
		rd->bf = EH;
		R_Rotate(&(*T)->rchild);
		L_Rotate(T);
		break;
	}
}

int InsertAVL(PBSTree* T,ElemType e,bool* taller)
{
	if ((*T)==NULL)
	{
		(*T)=(PBSTree)malloc(sizeof(BSTNode));
		(*T)->bf = EH;
		(*T)->data = e;
		(*T)->lchild = NULL;
		(*T)->rchild = NULL;
	}
	else if (e == (*T)->data)
	{
		*taller = false;
		return 0;
	}
	else if (e < (*T)->data)
	{
		if(!InsertAVL(&(*T)->lchild,e,taller))
			return 0;
		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)->rchild,e,taller))
			return 0;
		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 1;
}

bool FindNode(PBSTree root,ElemType e,PBSTree* pos)
{
	PBSTree pt = root;
	(*pos) = NULL;
	while(pt)
	{
		if (pt->data == e)
		{
			//找到节点,pos指向该节点并返回true
			(*pos) = pt;
			return true;
		}
		else if (pt->data>e)
		{
			pt = pt->lchild;
		}
		else
			pt = pt->rchild;
	}
	return false;
}
void InorderTra(PBSTree root)
{
	if(root->lchild)
		InorderTra(root->lchild);
	printf("%d ",root->data);
	if(root->rchild)
		InorderTra(root->rchild);
}

int main()
{
	int i,nArr[] = {1,23,45,34,98,9,4,35,23};
	PBSTree root=NULL,pos;
	bool taller;
	for (i=0;i<9;i++)
	{
		InsertAVL(&root,nArr[i],&taller);
	}
	InorderTra(root);
	if(FindNode(root,103,&pos))
		printf("\n%d\n",pos->data);
	else
		printf("\nNot find this Node\n");
	return 0;
}


 

### AVL树的定义 AVL树是一种自平衡二叉搜索树,以其发明者G.M.Adelson-Velsky和E.M.Landis的名字命名[^3]。它通过维护树的高度平衡来确保操作的时间复杂度保持在O(log n)级别。具体来说,在AVL树中,任意节点的两个子树的高度差(称为平衡因子)最多为1。 #### 平衡条件 - 对于任何一个节点,其左子树和右子树都必须是AVL树。 - 节点的平衡因子范围限定为{-1, 0, 1}。 --- ### AVL树的实现 以下是基于C++语言的一个典型AVL树实现框架: #### 结构体定义 ```cpp struct AVLNode { int key; int height; AVLNode* left; AVLNode* right; AVLNode(int k) : key(k), height(1), left(nullptr), right(nullptr) {} }; ``` #### 获取高度函数 用于计算某个节点的高度。 ```cpp int getHeight(AVLNode* node) { if (node == nullptr) return 0; return node->height; } ``` #### 更新高度函数 每次修改树结构后都需要更新节点的高度。 ```cpp void updateHeight(AVLNode* node) { if (node != nullptr) { node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1; } } ``` #### 左旋操作 当右子树过重时执行左旋。 ```cpp AVLNode* rotateLeft(AVLNode* y) { AVLNode* x = y->right; AVLNode* T2 = x->left; // 执行旋转 x->left = y; y->right = T2; // 更新高度 updateHeight(y); updateHeight(x); return x; // 新的根节点 } ``` #### 右旋操作 当左子树过重时执行右旋。 ```cpp AVLNode* rotateRight(AVLNode* x) { AVLNode* y = x->left; AVLNode* T2 = y->right; // 执行旋转 y->right = x; x->left = T2; // 更新高度 updateHeight(x); updateHeight(y); return y; // 新的根节点 } ``` #### 插入操作 插入过程中可能会破坏平衡,因此需要进行相应的旋转调整。 ```cpp AVLNode* insert(AVLNode* root, int key) { if (root == nullptr) { return new AVLNode(key); } if (key < root->key) { root->left = insert(root->left, key); } else if (key > root->key) { root->right = insert(root->right, key); } else { return root; // 不允许重复键值 } // 更新当前节点的高度 updateHeight(root); // 计算平衡因子并判断是否失衡 int balanceFactor = getHeight(root->left) - getHeight(root->right); // 进行必要的旋转调整 if (balanceFactor > 1 && key < root->left->key) { return rotateRight(root); // 单右旋 } if (balanceFactor < -1 && key > root->right->key) { return rotateLeft(root); // 单左旋 } if (balanceFactor > 1 && key > root->left->key) { root->left = rotateLeft(root->left); // 先左旋再右旋 return rotateRight(root); } if (balanceFactor < -1 && key < root->right->key) { root->right = rotateRight(root->right); // 先右旋再左旋 return rotateLeft(root); } return root; } ``` --- ### 应用场景 AVL树适用于那些频繁查询而较少插入或删除的应用场合。由于其严格的平衡约束,使得查找效率非常高,但在大量动态数据集中的表现可能不如红黑树灵活。常见的应用领域包括但不限于: - 数据库索引设计:快速定位记录的位置。 - 编译器优化:存储符号表以便高效访问变量信息。 - 文件系统的元数据管理:加速文件检索速度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值