之前学习到的二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此为了解决这一问题,发明了一种新方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树概念
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度 O ( l o g 2 n ) O(log_2 n) O(log2n)。
AVL树实现
AVL树基础结构
- AVL树节点定义
//AVL树节点定义
template <class T>
struct AVLNode
{
T _val;
//平衡因子
int _bf;
typedef AVLNode<T> Node;
Node* _parent;
Node* _left;
Node* _right;
AVLNode(const T& val = T())
:_val(val)
,_bf(0)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
- AVL树定义
template <class T>
class AVLTree
{
public:
typedef AVLNode<T> Node;
private:
Node* _root = nullptr;
};
插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
bool insert(const T& val)
{
if (_root == nullptr)
{
_root = new Node(val);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
parent = cur;
if (cur->_val == val)
return false;
else if (cur->_val > val)
cur = cur->_left;
else
cur = cur->_right;
}
cur = new Node(val);
if (parent->_val > val)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//调整,从parent开始
while (parent)
{
//更新parent的平衡因子,如果在左子树插入新节点-1,如果右子树插入+1
if (parent->_left == cur)
--parent->_bf;
else
++parent->_bf;
//直到父节点的平衡因子为0时停止更新(平衡因子为0说明插入新节点对祖先父节点的平衡因子不会有影响)
if (parent->_bf == 0)
//停止更新
break;
//如果平衡因子不为0,则继续向上更新
else if (parent->_bf == 1 || parent == -1)
{
cur = parent;
parent = parent->_parent;
}
else if (abs(parent->_bf) == 2)
{
if (parent->_bf == -2 && cur->_bf == -1)
{
//左边的左边高
//右旋
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
//右边的右边高
//左旋
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
//保存subLR的bf
Node* subLR = cur->_left;
int bf = subRL->_bf;
//左边的右边高
//先左旋再右旋
RotateL(cur);
RotateR(parent);
//修正平衡因子
if (bf == 1)
{
cur->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
parent->_bf = 1;
}
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
//保存subRL的bf
Node* subRL = cur->_left;
int bf = subRL->_bf;
//右边的左边高
//先右旋再左旋
RotateR(cur);
RotateL(parent);
//修正平衡因子
if (bf == 1)
{
cur->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
parent->_bf = 0;
}
}
break;
}
}
return true;
}
插入:右旋实现
由于平衡因子只能是-1,0或1,因此当平衡因子为-2时,出现了左子树的左边过高的问题,因此需要进行右旋操作使得二叉搜索树重新平衡。
因此进行过右旋操作后,47(-2)节点和35(-1)节点的平衡因子都变成0。
//右旋操作结构如下:
// parent(-2)
//subL(-1)
// subLR(0)
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
//更新cur和父亲的父亲之间的连接
//判断是否为根节点
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
Node* pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
//更新父亲的父亲为cur
parent->_parent = subL;
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
插入:左旋实现
由于平衡因子只能是-1,0或1,因此当平衡因子为2时,出现了右子树的右边过高的问题,因此需要进行左旋操作使得二叉搜索树重新平衡。
过程与上面右旋类似。
//左旋操作结构如下:
//parent(2)
// subR(1)
// subRL(0)
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//更新cur和父亲的父亲之间的连接
//判断是否为根节点
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
Node* pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
//更新父亲的父亲为cur
parent->_parent = subR;
//更新平衡因子
subR->_bf = parent->_bf = 0;
}
插入:左右双旋实现
- 插入节点后右边的左边高:单纯使用左旋或者右旋不能解决。
因此需要左右双旋:
1.以cur为轴,进行右旋
2.以parent为轴,进行左旋
- 左边的右边高:单纯使用左旋或者右旋不能解决。
因此需要左右双旋:
1.以cur为轴,进行左旋
2.以parent为轴,进行右旋
测试是否为平衡二叉树
- 检查平衡因子
- 查看平衡因子是否和左右子树的高度差一致
//计算树的高度
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->right);
return left > right ? left + 1 : right + 1;
}
//测试是否为平衡二叉树
bool _isBalance(Node* root)
{
if (root == nullptr)
return true;
//查看平衡因子是否和左右子树的高度差一致
int left = Height(root->_left);
int right = Height(root->_right);
if (right - left != root->_bf)
{
cout << "Node;" << root->_val << "bef:" << root->_bf << "height gap:" << right - left << endl;
return false;
}
return abs(root->_bf) < 2
&& _isBalance(root->_left)
&& _isBalance(root->_right);
}
AVL树完整实现代码:
//AVL树节点定义
template <class T>
struct AVLNode
{
T _val;
//平衡因子
int _bf;
typedef AVLNode<T> Node;
Node* _parent;
Node* _left;
Node* _right;
AVLNode(const T& val = T())
:_val(val)
,_bf(0)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
template <class T>
class AVLTree
{
public:
typedef AVLNode<T> Node;
bool insert(const T& val)
{
if (_root == nullptr)
{
_root = new Node(val);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
parent = cur;
if (cur->_val == val)
return false;
else if (cur->_val > val)
cur = cur->_left;
else
cur = cur->_right;
}
cur = new Node(val);
if (parent->_val > val)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//调整,从parent开始
while (parent)
{
//更新parent的平衡因子,如果在左子树插入新节点-1,如果右子树插入+1
if (parent->_left == cur)
--parent->_bf;
else
++parent->_bf;
//直到父节点的平衡因子为0时停止更新(平衡因子为0说明插入新节点对祖先父节点的平衡因子不会有影响)
if (parent->_bf == 0)
//停止更新
break;
//如果平衡因子不为0,则继续向上更新
else if (parent->_bf == 1 || parent == -1)
{
cur = parent;
parent = parent->_parent;
}
else if (abs(parent->_bf) == 2)
{
if (parent->_bf == -2 && cur->_bf == -1)
{
//左边的左边高
//右旋
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)
{
//右边的右边高
//左旋
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
//保存subLR的bf
Node* subLR = cur->_left;
int bf = subRL->_bf;
//左边的右边高
//先左旋再右旋
RotateL(cur);
RotateR(parent);
//修正平衡因子
if (bf == 1)
{
cur->_bf = -1;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
parent->_bf = 1;
}
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
//保存subRL的bf
Node* subRL = cur->_left;
int bf = subRL->_bf;
//右边的左边高
//先右旋再左旋
RotateR(cur);
RotateL(parent);
//修正平衡因子
if (bf == 1)
{
cur->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
parent->_bf = 0;
}
}
break;
}
}
return true;
}
//右旋操作结构如下:
// parent(-2)
//subL(-1)
// subLR(0)
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
//更新cur和父亲的父亲之间的连接
//判断是否为根节点
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
Node* pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
//更新父亲的父亲为cur
parent->_parent = subL;
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
//右旋操作结构如下:
// parent(-2)
//subL(-1)
// subLR(0)
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
//更新cur和父亲的父亲之间的连接
//判断是否为根节点
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
Node* pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
//更新父亲的父亲为cur
parent->_parent = subL;
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
//左旋操作结构如下:
//parent(2)
// subR(1)
// subRL(0)
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subL->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//更新cur和父亲的父亲之间的连接
//判断是否为根节点
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
Node* pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
//更新父亲的父亲为cur
parent->_parent = subR;
//更新平衡因子
subR->_bf = parent->_bf = 0;
}
void inorder()
{
_inorder(_root);
cout << endl;
}
void _inorder(Node* root)
{
if (root)
{
_inorder(root->_left);
cout << root->_val << " ";
_inorder(root->_right);
}
}
//计算树的高度
int Height(Node* root)
{
if (root == nullptr)
return 0;
int left = Height(root->_left);
int right = Height(root->right);
return left > right ? left + 1 : right + 1;
}
//测试是否为平衡二叉树
bool _isBalance(Node* root)
{
if (root == nullptr)
return true;
//查看平衡因子是否和左右子树的高度差一致
int left = Height(root->_left);
int right = Height(root->_right);
if (right - left != root->_bf)
{
cout << "Node;" << root->_val << "bef:" << root->_bf << "height gap:" << right - left << endl;
return false;
}
return abs(root->_bf) < 2
&& _isBalance(root->_left)
&& _isBalance(root->_right);
}
private:
Node* _root = nullptr;
};
void test()
{
srand(time(nullptr));
cout << "num: " << endl;
int num;
cin >> num;
AVLTree<int> avl;
for (int i = 0; i < num; ++i)
{
avl.insert(rand());
}
avl.inorder();
}