AVL树——特殊的二叉查找树

一、基本含义

1.AVL树
AVL树是一种特殊形式的二叉查找树,其任何结点的两个子树的高度差不超过1,因此也被称为高度平衡树
2.平衡因子
结点的左子树与右子树的高度差称为平衡因子,AVL树中所有结点的平衡因子可取:1,0,-1
3.AVL树结点的类型定义

//AVL树的结点类型定义
typedef struct avlNode {
	int ket;
	int h;//以当前结点为根结点的AVL树的高度
	avlNode* lc, * rc;
	avlNode():h(1),lc(NULL),rc(NULL){}
}*avlTree;
//获取子树高度
int avl_height(avlTree avl) {
	return avl == NULL ? 0 : avl->h;
}

3.不平衡结点
假设出现不平衡的结点为A,则平衡处理一般涉及结点A、A的某个孩子结点B以及B的某个孩子结点C。这三个结点有如下性质:
(1)如果结点A的另一个孩子结点为 B 1 B_1 B1,则 ∣ h e i g h t ( B ) − h e i g h t ( B 1 ) ∣ = 2 |height(B)-height(B_1)|=2 height(B)height(B1)=2
(2)如果结点B的另一个孩子结点为 C 1 C_1 C1, h e i g h t ( C ) > = h e i g h t ( C 1 ) height(C)>=height(C_1) height(C)>=height(C1)

二、AVL树的平衡处理

1.LL旋转

1.含义
当B为A的左孩子且C为B的左孩子时进行的平衡处理类型称为LL旋转
2.方法
将结点A的左孩子重新设置为B的右孩子 B r B_r Br,将B的右孩子重新设置为结点A,此时以结点A和B为根结点的子树的高度发生变化。LL旋转后子树的根结点变为B

//AVL树的LL旋转操作,返回旋转后子树的根结点
avlTree LL(avlTree A) {
	avlTree B = A->lc;
	A->lc = B->rc;
	B->rc = A;
	A->h = max(avl_height(A->lc), avl_height(A->rc)) + 1;//更新结点A的高度
	B->h = max(avl_height(B->lc), avl_height(B->rc)) + 1;//更新结点B的高度
	return B;
}

2.RR旋转

1.含义
当B为A的右孩子且C为B的右孩子时进行的平衡处理类型称为RR旋转,此时A的平衡因子为-2
2.方法
将结点A的右孩子重新设置为结点B的左孩子 B l B_l Bl,将结点B的左孩子重新设置为结点A,并更新结点A和B为根结点的子树的高度。RR旋转后子树的根结点变为B\

//AVL树的RR旋转操作,返回旋转后子树的根结点
avlTree RR(avlTree A) {
	avlTree B = A->rc;
	A->rc = B->lc;
	B->lc = A;
	A->h = max(avl_height(A->lc), avl_height(A->rc)) + 1;
	B->h = max(avl_height(B->lc), avl_height(B->rc)) + 1;
	return B;
}

3.LR旋转

1.含义
当B为A的左孩子且C为B的右孩子时进行的平衡处理类型称为LR旋转
2.方法
先对结点B和C进行RR旋转,然后对结点A和C进行LL旋转。LR旋转后子树的根结点为C

//AVL树的LR旋转操作,返回旋转后子树的根结点
avlTree LR(avlTree A) {
	A->lc = RR(A->lc);
	return LL(A);
}

4.LR旋转

1.含义
当B为A的右孩子且C为B的左孩子时进行的平衡处理类型称为RL旋转,此时A的平衡因子为-2
2.方法
先对B结点和C进行LL旋转,然后对结点A和C进行RR旋转,RL旋转后子树的根结点为C

//AVL树的RL旋转操作
avlTree RL(avlTree A) {
	A->rc = LL(A->rc);
	return RR(A);
}

三、AVL树的插入操作

1.算法1-1:AVL树的插入操作
功能
在AVL树t中插入一个键值为k的结点
算法步骤
(1)创建一个新的结点tmp,其键值为k,高度为1,按照BST插入结点的方法,将tmp插入AVL树中
(2)从tmp出发,沿根结点方向向上回溯,在回溯过程中,更新所经过结点的高度,并检查其平衡因子,如果平衡因子不为2或-2,继续向上回溯,否则进入第三步
(3)假设当前结点为A,分为两种情况
(3.1)A的前一个结点B为A的左孩子,分为两种情况
(3.1.1)如果k<结点B的键值,则插入结点的位置为B的左子数,对A和B进行LL旋转
(3.1.2)如果k>结点B的键值,则插入结点的位置为B的右子树,设B的右孩子为C,对A,B,C进行LR旋转
(3.2)A的前一个结点B为A的右孩子,分为两种情况
(3.2.1)如果k>结点B的键值,则插入结点的位置为B的右子数,对A和B进行RR旋转
(3.2.2)如果k<结点B的键值,则插入结点的位置为B的左子树,设B的左孩子为C,对A,B,C进行RL旋转

//向AVL树t中插入结点,结点的键值为k,插入成功,返回true,否则,返回false
bool avl_insert(avlTree& t, int k) {
	if (t == NULL) {
		t = new avlNode;
		t->key = k;
		return true;
	}
	if (k < t->key) {//插入结点位于t的左子树
		avl_insert(t->lc, k);//插入t的左子数
		if (avl_height(t->lc) - avl_height(t->rc) > 1) {//回退时检查t子树是否平衡
			//不平衡,A为t,B为t->lc,因为此处属于t的左子树回退部分
			if (t->lc->key > k)t = LL(t);//C为B的左子数
			else t = LR(t);//C为B的右子树
		}
	}
	else if (k > t->key) {//插入结点位于t的右子树
		avl_insert(t->rc, k);//插入t的右子树
		if (avl_height(t->rc) - avl_height(t->lc) > 1) {//回退时检测t子树是否平衡
			//不平衡,A为t,B为t->rc,因为此处属于t的右子树回退部分
			if (t->rc->key < k)t = RR(t);//C为B的右子树
			else t = RL(t);//C为B的左子树
		}
	}
	else
		return false;
	t->h = max(avl_height(t->lc), avl_height(t->rc)) + 1;//在回退时更新t的高度
}

四、AVL树的删除操作

1.算法1-2:AVL树的删除
功能
在AVL树t中删除键值为k的结点
算法步骤
(1)按照BST删除结点的方法,删除AVL树中键值为k的结点,最终删除的结点一定是叶结点,假设为tmp
(2)从tmp出发,沿着结点方向向上回溯,在回溯过程中,更新所经过结点的高度,并检查平衡因子,如果平衡因子不为2或-2,继续向上回溯,否则进入第三步
(3)假设当前结点为A,分为如下两种情况
(3.1)A的前一个结点为A的左孩子,设B为A的右孩子,又分为如下情况
(3.1.1)如果B的右子树的高度>=B的左子树的高度,对A和B进行RR旋转
(3.1.2)如果B的右子树的高度<B的左子树的高度,则设B的左孩子为C,对A,B,C进行RL旋转
(3.2)A的前一个结点为A的右孩子,设B为A的左孩子,又分为如下两种情况
(3.2.1)如果B的左子树的高度>=B的右子树的高度,对A和B进行LL旋转
(3.2.2)如果B的左子树的高度<B的右子树的高度,设B的右孩子为C,对A,B,C进行LR旋转

//删除AVL树t中键值为k的结点。删除成功返回true,否则返回false
bool avl_delete(avlTree& t, int k) {
	static bool ret = true;
	if (t == NULL)return false;
	else if (k < t->key) {//删除结点在t的左分支
		avl_delete(t->lc, k);
		if (avl_height(t->rc) - avl_height(t->lc) > 1) {//判断t是否平衡
			//不平衡,A为t,B为A的右孩子,因为此处属于t的左子树回退部分
			avlTree B = t->rc;
			if (avl_height(B->rc) >= avl_height(B->lc))t = RR(t);//C为B的右孩子
			else t = RL(t);//C为B的左孩子
		}
	}
	else if (k > t->key) {//删除结点在t的右分支
		avl_delete(t->rc, k);
		if (avl_height(t->lc) - avl_height(t->rc) > 1) {//判断t是否平衡
			avlTree B = t->lc;
			if (avl_height(B->lc) >= avl_height(t->rc))t = LL(t);//C为B的左孩子
			else t = LR(t);//C为B的右孩子
		}
	}
	else {//结点t的键值等于k,删除结点t
		if (t->lc == NULL || t->rc == NULL) {
			delete t;
			t = NULL;//叶结点直接删除
		}
		else if (t->rc == NULL) {//t没有右孩子
			avlNode* tmp = t->rc;
			int k1 = tmp->key;
			avl_delete(t, k1);//删除t的左孩子
			t->key = k1;//用t的左孩子的键值取代t的键值
		}
		else {//t的右子树不为空
			avlNode* tmp = t->rc;
			while (tmp->lc)tmp = tmp->lc;//获取t的中跟序列的直接前驱
			int k1 = tmp->key;
			avl_delete(t, k1);//删除tmp
			t->key = k1;//用tmp的键值取代t的键值
		}
		ret = true;
	}
	if (t)
		t->h = max(avl_height(t->lc), avl_height(t->rc)) + 1;
	return ret;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值