本文已参与「新人创作礼」活动,一起开启掘金创作之路。
平衡二叉树
平衡二叉树首先具备了二叉搜索树的特性,因为二叉搜索树并未对树的高做限制,只要求了左小右大,这就可能在极端情况下,出现左斜树或右斜树,这是查找就从O(logn)的期望退化成了O(n),即使不那么极端,树的高度也是没办法控制在一个稳定值上,这就使得当数据量庞大时,查找耗时过多。
这就产生了如何对一棵树进行优化,使其高度稳定,首先需要对树的高度进行获取和记录,通过左旋和右旋使二叉搜索树达到平衡,也就是使各节点左子树和右子树的高度差不超过1,这样的查找时间复杂度就能稳定在O(logn)级别;
1. 定义
“平衡因子(Balanced Factor,简称BF)”:BF(T) =Hl - Hr
,其中Hl 和Hr分别为T的左、右子树的高度。
平衡二叉树(AVL树):空树或者任一结点左、右子树高度差的绝对值不超过1的搜索二叉树,即|BF(T)| <= 1
AVL树的最大高度能达到logn吗?
给定结点数为 n 的AVL树的最大高度为O(logn)!
2. 平衡调整
-
右单旋:“麻烦结点” 在 不平衡的“发现者” 右子树的右边,因而叫 RR插入 ,需要 RR旋转
如下图,在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。
这种情况调整如下:
- 将A的右孩子B提升为新的根结点;
- 将原来的根结点A降为B的左孩子
- 各子树按大小关系连接(AL和BR不变,BL调整为A的右子树)。
-
左单旋:“麻烦结点”* 在 不平衡的“发现者” 左子树的左边,因而叫 LL插入 ,需要 LL旋转
在A的左孩子B的左子树BL(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。
这种情况调整如下:
- 将A的左孩子B提升为新的根结点;
- 将原来的根结点A降为B的右孩子;
- 各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。
-
左右双旋:“麻烦结点” 在 不平衡的“发现者” 左子树的右边,因而叫 LR插入 ,需要 LR旋转
在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。
这种情况调整如下:
- 将B的右孩子C提升为新的根结点;
- 将原来的根结点A降为C的右孩子;
- 各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。
-
右左双旋:“麻烦结点” 在 不平衡的“发现者” 右子树的左边,因而叫 RL插入 ,需要 RL旋转
在A的右孩子B的左子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。
这种情况调整如下:
- 将B的左孩子C提升为新的根结点;
- 将原来的根结点A降为C的左孩子;
- 各子树按大小关系连接(AL和BR不变,CL和CR分别调整为A的右子树和B的左子树)。
3. 代码实现
// 对着图去旋转会很清晰
#include <iostream>
using namespace std;
typedef int ElemType;
struct AVLNode {
ElemType data;
int height; // 高度,通过高度的比较可以得到是否平衡
AVLNode* left;
AVLNode* right;
AVLNode(ElemType data = 0) :
data(data), height(1), left(nullptr), right(nullptr) { }
};
// 获取树的最大高度
int getHeightAVL(AVLNode* T)
{
if (!T) {
return 0;
}
return 1+ max(getHeightAVL(T->left), getHeightAVL(T->right));
}
// LL旋转
// 返回新父结点,T为发现失衡的最低的结点
AVLNode* LL_rotate(AVLNode* T)
{
AVLNode* newT = T->left;
T->left = newT->right;
newT->right = T;
// 高度必须重新获取
T->height = 1 + max(getHeightAVL(T->left), getHeightAVL(T->right));
newT->height = 1 + max(getHeightAVL(newT->left), getHeightAVL(newT->right));
return newT;
}
// RR旋转
AVLNode* RR_rotate(AVLNode* T)
{
AVLNode* newT = T->right;
T->right = newT->left;
newT->left = T;
T->height = 1 + max(getHeightAVL(T->left), getHeightAVL(T->right));
newT->height = 1 + max(getHeightAVL(newT->left), getHeightAVL(newT->right));
return newT;
}
// LR旋转
// 先RR旋转,然后LL旋转
AVLNode* LR_rotate(AVLNode* T)
{
AVLNode* new1T = T->left;
T->left = RR_rotate(new1T);
return LL_rotate(T);
}
// RL旋转
// 先LL旋转,然后RR旋转
AVLNode* RL_rotate(AVLNode* T)
{
AVLNode* new1T = T->right;
T->right = LL_rotate(new1T);
return RR_rotate(T);
}
// 判断是否平衡
int getBalanceAVL(AVLNode* T)
{
if (!T) {
return 0;
}
return getHeightAVL(T->left) - getHeightAVL(T->right);
}
// 插入结点
AVLNode* insertAVL(AVLNode* node, ElemType key)
{
// 先向AVL树中插入key
if (!node) {
node = new AVLNode(key);
return node;
}
if (key < node->data) {
node->left = insertAVL(node->left, key);
}
else if (key > node->data) {
node->right = insertAVL(node->right, key);
}
else {
return node;
}
// 重新调整结点的高度
node->height = 1 + max(getHeightAVL(node->left), getHeightAVL(node->right));
// 插入之后可能发生不平衡的状况,开始调整AVL树
int balance = getBalanceAVL(node);
if (balance > 1 && key < node->left->data) // LL型
return LL_rotate(node);
if (balance > 1 && key > node->left->data) // LR型
return LR_rotate(node);
if (balance < -1 && key > node->right->data) // RR型
return RR_rotate(node);
if (balance < -1 && key < node->right->data) // RL型
return RL_rotate(node);
return node;
}
// 删除操作
// 找到最小的结点
AVLNode* minDataNode(AVLNode* node)
{
AVLNode* cur = node;
while (cur->left) {
cur = cur->left;
}
return cur;
}
AVLNode* deleteNode(AVLNode* node, ElemType key)
{
if (!node) { // 空树
return node;
}
if (key < node->data) {
node->left = deleteNode(node->left, key);
}
else if (key > node->data) {
node->right = deleteNode(node->right, key);
}
// 这里比较关键,比较绕需要梳理画图
else { // 现在已经找到要删除的结点了,去删除它
// 如果左子树为空或右子树为空
if ((!node->left) || (!node->right)) {
// temp指向存在的子树
AVLNode* temp = node->left ? node->left : node->right;
if (!temp) { // 左右子树都空
temp = node;
node = nullptr;
}
else {
// 这里并不是删除结点
// 而是将它存在的子树的结点值赋给我们要“删除”的结点
// 然后删除子树结点
*node = *temp;
}
delete temp;
}
// 如果左右子树都存在
else {
AVLNode* temp = minDataNode(node->right);
node->data = temp->data;
node->right = deleteNode(node->right, temp->data);
}
}
if (!node) {
return node;
}
// 重新调整结点的高度
node->height = 1 + max(getHeightAVL(node->left), getHeightAVL(node->right));
// 插入之后可能发生不平衡的状况,开始调整AVL树
int balance = getBalanceAVL(node);
if (balance > 1 && key < node->left->data) // LL型
return LL_rotate(node);
if (balance > 1 && key > node->left->data) // LR型
return LR_rotate(node);
if (balance < -1 && key > node->right->data) // RR型
return RR_rotate(node);
if (balance < -1 && key < node->right->data) // RL型
return RL_rotate(node);
return node;
}
参考:
-
salt.Zhang blog.csdn.net/qq_34930032…