C++ : AVL 树之 右左双旋(第四章)
AVL树最后一章啦,其实我已经将红黑树的博客也写出来咯,而且这次添加了很多代码注释,让你直接独代码也可以自己慢慢分析的哦
一、什么是 RL 双旋?解决什么问题?
RL 双旋是处理 RL 失衡 的组合操作 —— 当某节点的右子树高度比左子树高 2(父节点 bf=2
),但右孩子的左子树更高(右孩子 bf=-1
)时,直接对父节点做左单旋无法平衡,需要先对右孩子做右单旋,将结构 “掰直” 为 RR 失衡,再对原父节点做左单旋,最终实现树的平衡。
简单说:右子树呈现 “先右后左拐”(RL)的形态,先把 “拐弯处” 的节点(subRL
)提上来,将不规则的 RL 结构转化为规则的 RR 结构,再用左单旋完成平衡。
二、RL 失衡场景特征
要准确识别 RL 失衡,需抓住 父节点、右孩子、拐弯节点 的三个核心特征,结合具体例子理解更直观:
1. 场景示例(节点结构)
假设插入元素后,树的局部结构如下(括号内为节点 bf
值):
- 父节点
A(6, bf=2)
:右子树比左子树高 2,触发失衡判断; - 右孩子
B(8, bf=-1)
:A 的右子树,但其左子树比右子树高 1(拐弯的根源); - 拐弯节点
C(7, bf=0)
:B 的左子树(subRL
),插入点在 C 的子树中(如 C 的左孩子 5、右孩子 9); - 其他节点:A 的左子树
D(4, bf=0)
,B 的右子树E(9, bf=0)
。
结构示意图(失衡前):
A(6, bf=2)/ \
D(4,0) B(8, bf=-1)/ \C(7,0) E(9,0)/
F(5,0) (插入点,导致C的左子树过高)
2. 核心特征(判断依据)
- 父节点
bf=2
:右子树高度比左子树高 2,满足失衡触发条件; - 右孩子
bf=-1
:父节点的右子树 “向左拐”(左子树更高),而非 “纯右偏”; - 失衡根源:右孩子的左子树(
subRL
)过高,直接左单旋会导致subRL
的子树 “挂错父节点”,无法修复高度差。
三、RL 双旋步骤拆解
RL 双旋与 LR 双旋逻辑对称,同样分 “两步走”:先旋右孩子(掰直结构),再旋父节点(平衡树)。每一步都需明确节点的父子关系变化,避免指针悬空。
步骤 1:对右孩子(B)做右单旋(转化为 RR 结构)
目标:将拐弯节点 C(7)
提为右孩子 B(8)
的父节点,把 RL 结构转化为 RR 结构(纯右偏)。
具体操作(对应代码 RotateR(subR)
):
- 取右孩子
B
的左子树C(subRL)
,记录C
的右子树(若有,如C->_right
); - 将
C
的右子树交给B
当左子树(若C->_right
非空,需更新其_parent
为B
); - 将
B
降为C
的右孩子,更新B->_parent
为C
; - 将
C
提为原父节点A
的右孩子,更新C->_parent
为A
。
步骤 1 后结构(RR 结构):
plaintext
A(6, bf=2)/ \
D(4,0) C(7, bf=1) (原subRL,现在是A的右子树)/ \- B(8, bf=0) (原右孩子B,现在是C的右子树)\E(9,0)
此时,父节点 A
的右子树已变为 “纯右偏”(C 的右子树是 B,B 的右子树是 E),即 RR 失衡结构,可通过左单旋平衡。
步骤 2:对父节点(A)做左单旋(完成平衡)
目标:将拐弯节点 C(7)
提为父节点 A(6)
的父节点,消除 A
的高度差(bf=2
)。
具体操作(对应代码 RotateL(parent)
):
- 取父节点
A
的右子树C
,记录C
的左子树(若有); - 将
C
的左子树交给A
当右子树(若C->_left
非空,需更新其_parent
为A
); - 将
A
降为C
的左子树,更新A->_parent
为C
; - 若
A
原是根节点,更新_root
为C
;否则,将C
对接A
的原父节点(pparent
),维护树的整体链接。
步骤 2 后结构(最终平衡):
plaintext
C(7, bf=0)/ \
A(6,0) B(8, bf=0)\ \
D(4,0) E(9,0)
此时所有节点的 bf
均为 0,左子树与右子树高度一致,AVL 树规则恢复。
四、RL 双旋代码解析
结合你提供的 AVL 树源码,RotateRL
函数是 RL 双旋的核心实现。需重点关注 节点指针操作 和 平衡因子(bf)更新——bf 的更新直接决定后续插入是否能正确判断失衡。
1. 完整代码
// RL双旋:先对右孩子做右单旋,再对父节点做左单旋
void RotateRL(Node* parent)
{// 1. 定义关键节点:parent(失衡父节点)、subR(右孩子)、subRL(拐弯节点,subR的左子树)Node* subR = parent->_right; // 父节点的右孩子(如示例中的B(8))Node* subRL = subR->_left; // 拐弯节点(如示例中的C(7))int bf = subRL->_bf; // 保存subRL的原始bf!这是后续更新bf的关键依据// 2. 第一步:对右孩子subR做右单旋,将RL转化为RR结构RotateR(subR);// 3. 第二步:对父节点parent做左单旋,完成平衡RotateL(parent);// 4. 根据subRL的原始bf,更新三个核心节点的bf(parent、subR、subRL)// 核心逻辑:subRL的bf决定了插入点的位置,进而影响旋转后子树的高度差if (bf == -1) {// 情况1:subRL的bf=-1 → 插入点在subRL的右子树(如C的右孩子9)// 旋转后:subR(B)的左子树高度 < 右子树,bf=1;其他节点bf归0subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;} else if (bf == 1) {// 情况2:subRL的bf=1 → 插入点在subRL的左子树(如C的左孩子5)// 旋转后:parent(A)的右子树高度 < 左子树,bf=-1;其他节点bf归0subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;} else if (bf == 0) {// 情况3:subRL的bf=0 → subRL本身是插入节点(无孩子)// 旋转后:三个节点的子树高度完全一致,bf均归0subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;} else {// 异常情况:bf只能是-1、0、1,触发断言排查错误assert(false);}
}
2.细节解读
(1)为什么要保存 subRL->_bf
?
subRL
的原始 bf 直接反映 “插入点的位置”:
bf=1
:插入点在subRL
的左子树 → 旋转后父节点A
的左子树会偏高;bf=-1
:插入点在subRL
的右子树 → 旋转后右孩子B
的右子树会偏高;bf=0
:subRL
是新插入节点 → 旋转后所有子树高度平衡。若不保存原始 bf,无法精准更新旋转后节点的 bf,会导致后续插入误判失衡。
(2)旋转函数的复用性
RotateRL
中直接调用了已实现的 RotateR
(右单旋)和 RotateL
(左单旋),无需重复编写指针操作逻辑 —— 这是代码模块化的体现,也避免了重复错误。
五、bf 更新逻辑详解(核心难点)
RL 双旋的 bf 更新与 LR 双旋对称,但容易因 “方向混淆” 出错。需结合 “插入点位置” 和 “旋转后子树高度变化” 理解,以下用具体场景验证:
场景 1:subRL->_bf = 1(插入点在 subRL 左子树)
示例:插入 5
到 C(7)
的左子树,subRL(C)
的 bf 变为 1(左子树高 1)。
- 旋转后:
parent(A)
的右子树(原 C 的左子树)被提走,左子树(D (4))比右子树高 1 →A->_bf = -1
; subR(B)
的左子树为空,右子树(E (9))高 1 →B->_bf = 0
;subRL(C)
的左右子树(A 和 B)高度一致 →C->_bf = 0
。
场景 2:subRL->_bf = -1(插入点在 subRL 右子树)
示例:插入 9
到 C(7)
的右子树,subRL(C)
的 bf 变为 -1(右子树高 1)。
- 旋转后:
subR(B)
的右子树(E (9))比左子树高 1 →B->_bf = 1
; parent(A)
的左右子树(D 和 C 的左子树)高度一致 →A->_bf = 0
;subRL(C)
的左右子树高度一致 →C->_bf = 0
。
场景 3:subRL->_bf = 0(subRL 是新插入节点)
示例:直接插入 7
作为 B(8)
的左子树,subRL(C)
的 bf 为 0(无孩子)。
- 旋转后:
A
、B
、C
的子树均无高度差 → 三者 bf 均为 0。
六、RL 双旋核心总结
- 适用场景:父节点
bf=2
+ 右孩子bf=-1
(RL 失衡); - 核心逻辑:先右旋右孩子(掰直为 RR 结构),再左旋父节点(平衡树);
- 关键注意:
- 必须保存
subRL
的原始 bf,用于精准更新旋转后节点的平衡因子; - 旋转时需维护所有节点的
_parent
指针,避免悬空(尤其是subRL
的子树和原父节点的祖先);
- 必须保存
- 与 LR 双旋的区别:方向完全对称,LR 是 “左子树先左后右”,RL 是 “右子树先右后左”,可对比记忆。
下面提供一下AVL树全部代码:
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
//AVL Tree;
namespace ym
{
template<class K, class V>
class AVLTreeNode
{
public:
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{
}
};
//K->key(比较一般使用关键词key)
template<class K, class V>
class AVLTree
{
using Node = AVLTreeNode<K, V>;
public:
AVLTree() = default; //强制生成默认构造
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
else
{
Node* parent = nullptr;
Node* pcur = _root;
while (pcur)
{
if (pcur->_kv.first < kv.first)
{
parent = pcur;
pcur = pcur->_right;
}
else if (pcur->_kv.first > kv.first)
{
parent = pcur;
pcur = pcur->_left;
}
else
{
return false;
}
}
pcur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = pcur;
}
else
{
parent->_left = pcur;
}
// 链接父亲
pcur->_parent = parent;
//更新平衡因子
while (parent)
{
if (pcur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
break; //任然是AVL树
}
else if (parent->_bf == -1 || parent->_bf == 1)
{
pcur = parent;
parent = parent->_parent; //继续更新
}
else if (parent->_bf == -2 || parent->_bf == 2)
{
//旋转
if (parent->_bf == -2 && pcur->_bf == -1) //左边多且只插入到左子树的情况,即左边为h + 1, 右边为h,插入左边(h + 1)的左边
{
RotateR(parent);
}
else if (parent->_bf == 2 && pcur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && pcur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && pcur->_bf == -1)
{
RotateRL(parent);
}
else
{
assert(false);
}
//退出
break;
}
else //如果在之前就已经不是AVL树,则返回assert报错
{
assert(false);
}
}
return true;
}
}
// 单旋是纯粹的一边高
void RotateR(Node* parent) //左边多,右单旋
{
Node* subL = parent->_left; //左边
Node* subLR = subL->_right; //左边的右边
Node* pparent = parent->_parent; //父亲的父亲节点
// 6(p)
// 4(L) 7
// 3 5(LR)
//(插入)
parent->_left = subLR;
if (subLR) //非空就可以修改
subLR->_parent = parent;
parent->_parent = subL;
subL->_right = parent;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subL;
}
else
{
pparent->_right = subL;
}
subL->_parent = pparent;
}
subL->_bf = 0;
parent->_bf = 0;
}
void RotateL(Node* parent) //右边多,左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pparent = parent->_parent;
if (subRL)
subRL->_parent = parent;
parent->_right = subRL;
subR->_left = parent;
parent->_parent = subR;
if (_root == parent)
{
subR->_parent = nullptr;
_root = subR;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subR;
}
else
{
pparent->_right = subR;
}
subR->_parent = pparent;
}
parent->_bf = 0;
subR->_bf = 0;
}
//左右双旋(左拐右拐),先对subL进行左单旋,在对parent进行右单旋即可
// 4
// 3
//Null (2)插入
// 左单旋
// 4
// 3
// 2 NULL
//右单旋
// 3
// 2 4
void RotateLR(Node* parent) //左右单旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
void RotateRL(Node* parent) //右左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
bool Find(const K& key)
{
Node* pcur = _root;
while (pcur)
{
if (pcur->_kv.first < key)
{
pcur = pcur->_right;
}
else if (pcur->_kv.first > key)
{
pcur = pcur->_left;
}
else
{
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
}
int Height()
{
return _Height(_root);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
private:
void _InOrder(const Node* root)
{
if (root == nullptr)
{
//cout << "Nullptr ";
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalanceTree(Node* root)
{
//空树也是AVL树
if (nullptr == root)
return true;
//计算pRoot结点的平衡因⼦:即pRoot左右⼦树的⾼度差
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
//如果计算出的平衡因⼦与pRoot的平衡因⼦不相等,或者
//pRoot平衡因⼦的绝对值超过1,则⼀定不是AVL树
if (abs(diff) >= 2)
{
cout << root->_kv.first << "⾼度差异常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因⼦异常" << endl;
return false;
}
// pRoot的左和右如果都是AVL树,则该树⼀定是AVL树
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
Node* _root = nullptr;
};
}
void TestAVLTree1()
{
ym::AVLTree<int, int> t;
// 常规的测试⽤例
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
// 特殊的带有双旋场景的测试⽤例
//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto& e : a)
{
t.Insert({ e, e });
}
t.InOrder();
cout << t.IsBalanceTree() << endl;
}
int main()
{
TestAVLTree1();
return 0;
}