平衡二叉树

0. 为何要使用AVL树?

二叉搜索树的搜索效率与其树的深度相关,而二叉搜索树的组成又与其插入序列相关,在极端情况下,二叉搜索树退化为一条单链(比如插入序列是 1 2 3 … n),使得搜索效率大大降低,为了避免这种情况出现,我们采用二叉平衡树对插入结点进行调整,使得树的深度尽可能小

1. 定义

  • 平衡因子
    BF(T) = h <math> <semantics> <mrow> <msub> <mi> L </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _L </annotation> </semantics> </math>L - h <math> <semantics> <mrow> <msub> <mi> R </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _R </annotation> </semantics> </math>R,其中 h <math> <semantics> <mrow> <msub> <mi> L </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _L </annotation> </semantics> </math>L、h <math> <semantics> <mrow> <msub> <mi> R </mi> </msub> </mrow> <annotation encoding="application&#47;x&#45;tex"> _R </annotation> </semantics> </math>R 分别是左右子树的高度
  • 平衡二叉树(AVL 树)
    空树,或者任一结点左、右子树高度差的绝对值不超过 1,即 |BF(T)|≤1 的树

2. 平衡二叉树的调整

0. 遵循原则

  • 从离插入结点最近的结点调整

1. RR 单旋

当"插入结点"(BR)是"被破坏平衡结点"(A)右子树右子树时,即 RR 插入时,采用 RR 旋转调整

其基本思路是把 B 的左子树腾出来挂到 A 的右子树上,返回 B 作为当前子树的根

示意图:

AVLTree RRRotation(AVLTree A){
	AVLTree B = A->right;   // B 为 A 的右子树 
	A->right = B->left;    // B 的左子树挂在 A 的右子树上 
	B->left = A;   // A 挂在 B 的左子树上 
	return B;  // 此时 B 为根结点了 
}

2. LL 单旋

当"插入结点"(BL)是"被破坏平衡结点"(A)左子树左子树时,即 LL 插入,采用 RR 旋转调整

其基本思路是把 B 的右子树腾出来挂到 A 的左子树上,返回 B 作为当前子树的根

示意图:

AVLTree LLRotation(AVLTree A){
	// 此时根节点是 A 
	AVLTree B = A->left;  // B 为 A 的左子树 
	A->left = B->right;   // B 的右子树挂在 A 的左子树上 
	B->right = A;     // A 挂在 B 的右子树上 
	return B;  // 此时 B 为根结点了 
}

3. LR 双旋

当"插入结点"(CL 或者 CR)是"被破坏平衡结点"(A)左子树右子树时,即 LR 插入,采用 LR 旋转调整

基本思想是先将 B 作为根结点进行 RR 单旋转化为 LL 插入,再将 A 作为根结点进行 LL 单旋(先 RR 再 LL)

示意图:

AVLTree LRRotation(AVLTree A){
	// 先 RR 单旋
	A->left = RRRotation(A->left);
	// 再 LL 单旋 
	return LLRotation(A);
}

总结:叫 LR 双旋是从上到下看,而实际先 RR 单旋再 LL 单旋是从下往上的过程

4. RL 双旋

当"插入结点"(CL 或者 CR)是"被破坏平衡结点"(A)右子树左子树时,即 RL 插入,采用 RL 旋转调整

基本思想是先将 B 作为根结点进行 LL 单旋转化为 RR 插入,再将 A 作为根结点进行 RR单旋(先 LL 再 RR)

示意图:

AVLTree RLRotation(AVLTree A){
	// 先 LL 单旋
	A->right = LLRotation(A->right);
	// 再 RR 单旋 
	return RRRotation(A); 
}

总结:叫 RL 双旋是从上到下看,而实际先 LL 单旋再 RR 单旋是从下往上的过程

练手题

Root of AVL Tree