二叉搜索树虽可以缩短查找的效率,但 如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
而AVL树可以较好的解决上述问题:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树是一种 自平衡二叉搜索树,严格满足以下性质:
当右子树高的时候,平衡因子+1,当左子树高时,平衡因子-1
对上述的树,就需要通过旋转让其平衡
AVLTree
的各项功能#pragma once
template<class K, class V>
struct AVLTreeNode //节点
{
// 成员变量 和 成员函数
};
template<class K, class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node; //重命名
public:
// 公有成员函数
private:
// 私有成员函数
private:
// 成员变量
Node* _root = nullptr;
};
template<class K, class V>
struct AVLTreeNode //节点
{
AVLTreeNode<K, V>* _left; //指向左子树
AVLTreeNode<K, V>* _right; //指向右子树
AVLTreeNode<K, V>* _parent; //指向父节点
pair<K, V> _kv; //键值对
int _bf; //平衡因子
AVLTreeNode(const pair<K, V> & kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
template<class K, class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node; //重命名
public:
// 插入
bool Insert(const pair<K, V>& kv)
{}
// 中序遍历/打印
void InOrder()
{}
//判断是否平衡
bool IsBalance()
{}
private:
// 判断是否平衡
bool _IsBalance(Node* root)
{}
// 求AVL树最大高度
int Height(Node* root)
{}
// 左单旋
void RotateL(Node* parent)
{}
// 右单旋
void RotateR(Node* parent)
{}
// 左右双旋
void RotateLR(Node* parent)
{}
// 右左双旋
void RotateRL(Node* parent)
{}
void _InOrder(Node* root)
{}
private:
// 根节点
Node* _root = nullptr;
};
插入过程主要分为两个步骤:
bool Insert(const pair<K, V>& kv)
{
// 根为空时第一次插入直接创建新节点
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
// 搜索树的插入逻辑
while (cur) {
// 如果插入的节点值大,则插入右边
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// 如果要插入的节点的键值大于其父节点的键值,则将其作为右子节点;
// 否则将其作为左子节点。
cur = new Node(kv);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent; // 记录插入节点的父节点
// 控制平衡
// 1、更新平衡因子
while (parent)
{
// cur在右边,则平衡因子++
if (cur == parent->_right)
++parent->_bf;
// cur在左边,则平衡因子--
else
--parent->_bf;
// 平衡因子 == 0,已经平衡
if (parent->_bf == 0) {
break;
}
// == 1,向上找
else if (abs(parent->_bf) == 1) {
parent = parent->_parent;
cur = cur->_parent;
}
// == 2,此时parent所在子树已经不平衡了,需要旋转处理
else if (abs(parent->_bf) == 2) {
if (parent->_bf == 2 && cur->_bf == 1)
{
// 此时进行左单旋
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
// 此时进行右单旋
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
// 左右双旋
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
//右左双旋
RotateRL(parent);
}
else
{
// 正常情况下不会出现该种情况,如果出现直接报错
assert(false);
}
}
else {
// 正常情况下不会出现该种情况,如果出现直接报错
assert(false);
}
}
return true;
}
抽象图(思路):
void RotateL(Node* parent)
{
// 此时右子树的高度比左子树高2,记录右侧节点
Node* subR = parent->_right;
Node* subRL = subR->_left;
// 目的需要将subR变为新的父节点(根节点)
// 将SubRL变为parent的子节点
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
// 创建此时父节点的头节点
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR; //此时subR到父节点的位置
// 如果本来父节点为根
if (_root == parent)
{
//将subR变为根
_root = subR;
subR->_parent = nullptr;
}
// 本来parent不为根节点
else
{
// 将subR的_parent指向ppNode
if (ppNode->_left == parent)
ppNode->_left = subR;
else
ppNode->_right = subR;
subR->_parent = ppNode;
}
// 此时平衡,更新平衡因子
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
// 获取左子树和左子树的右子树
Node* subL = parent->_left;
Node* subLR = subL->_right;
// 旋转操作:将左子树的右子树变为 parent 的左子树
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
// 将 parent 变为左子树的右子树
subL->_right = parent;
parent->_parent = subL;
// 如果 parent 原本是根节点,则设置新的根节点为 subL,否则需要调整 parent 的父节点
Node* ppNode = parent->_parent;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
subL->_parent = ppNode;
}
// 更新平衡因子:旋转后,parent 和 subL 的平衡因子都变为 0
subL->_bf = parent->_bf = 0;
}
subL
进行一次左旋操作,此时 subL 变成了 parent 节点的父节点,subLR 成为了 subL 的右孩子。parent
节点进行一次右旋操作,此时 subLR 节点被转移到 parent 的位置上,并成为 parent 的父节点。同时,subLR 的右子树成为 parent 的左子树,subLR 的左子树成为 subL 的右子树。void RotateLR(Node* parent)
{
// 定义subL,subLR
Node* subL = parent->_left;
Node* subLR = subL->_right;
// 保存subLR节点的平衡因子
int bf = subLR->_bf;
// 左旋操作,将subLR上移到subL的位置
RotateL(parent->_left);
// 右旋操作,将subLR上移到parent的位置
RotateR(parent);
// 根据新树结构和子树高度调整各个节点平衡因子
subLR->_bf = 0; // subLR的平衡因子设为0
if (bf == 1) // 如果subLR原来的平衡因子为1
{
parent->_bf = 1;
subL->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
subL->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
}
// subLR的平衡因子取值范围应该是-1,0,1三个值,如果不在这个范围内则代表程序出现了错误
else
{
assert(false); // 断言,程序错误
}
}
void RotateRL(Node* parent)
{
// 定义subR,subRL
Node* subR = parent->_right;
Node* subRL = parent->_left;
// 保存subRL节点的平衡因子
int bf = subRL->_bf;
// 右旋操作,将subR上移到parent的位置
RotateR(parent->_right);
// 左旋操作,将subRL上移到parent的位置
RotateL(parent);
// 根据新树结构和子树高度调整各个节点平衡因子
subRL->_bf = 0; // subRL的平衡因子设为0
if (bf == 1) // 如果subRL原来的平衡因子为1
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
// subRL的平衡因子取值范围应该是-1,0,1三个值,如果不在这个范围内则代表程序出现了错误
else
{
assert(false);
}
}