AVL树是二叉搜索树的改进版本,思考一下,二叉搜索树有什么问题?
当我们向二叉搜索树里插入有序序列时,那么二叉搜索树就会退化成线性的,查找效率会变成O(n)
比如我们依次插入1,2,3,4,5,6,7
得到:
这是最坏情况,当然,只要插入数据时稍微有序,也会增加二叉搜索树的高度,所以我们需要寻找一个方法去解决这个问题!
这时就引入了AVL树~
两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
AVL树节点的定义:
template<class T>
struct AVLTreeNode
{
AVLTreeNode(const T& data)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _bf(0)
{}
AVLTreeNode<T>* _pLeft; // 该节点的左孩子
AVLTreeNode<T>* _pRight; // 该节点的右孩子
AVLTreeNode<T>* _pParent; // 该节点的双亲
T _data;
int _bf; // 该节点的平衡因子
};
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
新节点插入后,其父节点的平衡因子一定需要调整,在插入之前,父节点的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
新节点插入到父节点的左侧,只需给父节点的平衡因子-1即可新节点插入到父节点的右侧,只需给父节点的平衡因子+1即可此时:父节点的平衡因子可能有三种情况:0,正负1, 正负2
父节点的平衡因子为0,说明插入之前父节点的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功父节点的平衡因子为正负1,说明插入前父节点的平衡因子一定为0,插入后被更新成正负1,此时以父节点为根的树的高度增加,需要继续向上更新父节点的平衡因子为正负2,则父节点的平衡因子违反平衡树的性质,需要对其进行旋转处理此时主要问题在于调整平衡因子, AVL树引入旋转操作来降低树的平衡因子
在了解旋转操作之前,请读者复习一下
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:左单旋、右单旋、左右双旋、右左双旋
新节点插入较高右子树的右侧—右右:左单旋
void _RotateL(Node* parent) //传入平衡因子为2的节点指针
{
Node* subR = parent->_right;
Node* subRL = subR->_left; //这个节点是不一定存在的
Node* pparent = parent->_parent;
//有可能parent就是根节点,此时pparent节点也不存在
if (pparent)
{
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
}
else
{
_root = subR;
}
subR->_parent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
parent->_bf = subR->_bf = 0;
}
h
∈
[
0
,
+
∞
)
h \in [0, +\infty)
h∈[0,+∞)
在旋转时要注意:
新节点插入较高左子树的左侧—左左:右单旋
void _RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pparent = parent->_parent;
if (pparent)
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
}
else
{
_root = subL;
}
subL->_parent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_bf = subL->_bf = 0;
}
新节点插入较高左子树的右侧—左右:先左单旋再右单旋
void _RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
_RotateL(subL);
_RotateR(parent);
subLR->_bf = 0;
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
else if (bf == 0) //即subLR子树中只有一个节点
{
parent->_bf = subL->_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);
subRL->_bf = 0;
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = parent->_bf = 0;
}
else
assert(false);
}
验证可以分为两步:
bool IsLegal()
{
return _IsLegal(_root);
}
//判断是否合法
bool _IsLegal(Node* root)
{
if (root == nullptr) return true;
//平衡因子超过正负1非法
if (root->_bf >= 2 || root->_bf <= -2) return false;
if (!_IsLegal(root->_left) || !_IsLegal(root->_right)) return false;
return root->_bf == (Height(root->_right) - Height(root->_left));
}
//求子树高度
int Height(Node* root)
{
if (root == nullptr) return 0;
return std::max(Height(root->_left), Height(root->_right)) + 1;
}
测试用例:
常规场景1
{16, 3, 7, 11, 9, 26, 18, 14, 15}
特殊场景2
{4, 2, 6, 1, 3, 5, 15, 7, 16, 14}
#pragma once
#include <cassert>
#include <algorithm>
namespace moran {
template<class K,class V>
struct AVLTreeNode {
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
std::pair<K, V> _kv;
int _bf;
AVLTreeNode(const std::pair<K,V>& kv,int bf = 0, AVLTreeNode<K, V>* left = nullptr,
AVLTreeNode<K, V>* right = nullptr, AVLTreeNode<K, V>* parent = nullptr)
:_kv(kv),
_bf(bf),
_left(left),
_right(right),
_parent(parent)
{}
};
template <class K,class V>
class AVLTree {
typedef AVLTreeNode<K, V> Node;
public:
AVLTree() = default;
//迭代器区间构造
template<class InputIterator>
AVLTree(InputIterator first, InputIterator last)
{
while (first != last)
{
insert(*first);
}
}
bool Insert(const std::pair<K,V>& kv)
{
//根是否为空
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
if (cur->_left == nullptr)
{
Node* new_node = new Node(kv);
cur->_left = new_node;
new_node->_parent = cur;
//更新cur平衡因子
cur->_bf--;
break;
}
else
{
cur = cur->_left;
}
}
else if (cur->_kv.first < kv.first)
{
if (cur->_right == nullptr)
{
Node* new_node = new Node(kv);
cur->_right = new_node;
new_node->_parent = cur;
//更新cur的平衡因子
cur->_bf++;
break;
}
else
{
cur = cur->_right;
}
}
else
return false;
}
Node* parent = cur->_parent;
//如果cur平衡因子为0或者没有父节点,则无需继续更新
while (cur->_bf && parent)
{
if (cur->_bf == 1 || cur->_bf == -1)
{
if (cur == parent->_left)
{
parent->_bf--;
//需要调整
if (parent->_bf == -2)
{
if (cur->_bf == 1)
{
_RotateLR(parent);//左右旋
}
else
{
_RotateR(parent);//右旋
}
break;
}
}
else
{
parent->_bf++;
//需要调整
if (parent->_bf == 2)
{
if (cur->_bf == 1)
{
//左旋
_RotateL(parent);
}
else
{
_RotateRL(parent);//右左旋
}
break;
}
}
}
else
{
assert(false);
}
cur = parent;
parent = parent->_parent;
}
return true;
}
bool IsLegal()
{
return _IsLegal(_root);
}
private:
bool _IsLegal(Node* root)
{
if (root == nullptr) return true;
if (root->_bf >= 2 || root->_bf <= -2) return false;
if (!_IsLegal(root->_left) || !_IsLegal(root->_right)) return false;
return root->_bf == (Height(root->_right) - Height(root->_left));
}
int Height(Node* root)
{
if (root == nullptr) return 0;
return std::max(Height(root->_left), Height(root->_right)) + 1;
}
void _RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* pparent = parent->_parent;
if (pparent)
{
if (pparent->_left == parent)
pparent->_left = subL;
else
pparent->_right = subL;
}
else
{
_root = subL;
}
subL->_parent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
parent->_bf = subL->_bf = 0;
}
void _RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* pparent = parent->_parent;
if (pparent)
{
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
}
else
{
_root = subR;
}
subR->_parent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
parent->_bf = subR->_bf = 0;
}
void _RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
_RotateR(subR);
_RotateL(parent);
subRL->_bf = 0;
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = parent->_bf = 0;
}
else
assert(false);
}
void _RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
_RotateL(subL);
_RotateR(parent);
subLR->_bf = 0;
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = subL->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root = nullptr;
};
}
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即
l
o
g
2
(
N
)
log_2 (N)
log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,
比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
显然AVL树不适合作为STL的map、set底层数据结构,于是就有了对AVL树的改进:
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务