1.AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均 搜索长度。
AVL树具有以下性质:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,如果它有n个结点,其高度可保持在 O(logN),搜索时间复杂度O(logN),它就是AVL树。
2.AVL树节点的定义
pair是将2个数据组合成一组数据,如stl中的map就是将key和value放在一起来保存,或者当一个函数需要返回2个数据的时候,也可以选择pair。
pair的实现是一个结构体,主要的两个成员变量first和second,分别存储两个数据, 因为是使用struct不是class,所以可以直接使用pair的成员变量。
template<class k, class v >
struct AVLNode
{
pair<k, v> _kv;
AVLNode<k, v>* _left;
AVLNode<k, v>* _right;
AVLNode<k, v>* _parent;
//_pf记录高度差(右子树高度减去左子树高度)
int _pf;
AVLNode(const pair<k, v>& key)
:_kv(key)
, _left(nullptr)
, _right(nullptr)
,_parent(nullptr)
,_pf(0)
{}
};
3.AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么 AVL树的插入过程可以分为两步:
1. 按照二叉搜索树的方式插入新节点
2. 调整节点的平衡因子
//cur为新插入节点 prev为cur的双亲节点
while (prev != nullptr)
{
if (cur == prev->_left)
{
prev->_pf--;
}
else
{
prev->_pf++;
}
if (prev->_pf == 0)
{
break;
}
else if (prev->_pf == -1 || prev->_pf == 1)
{
cur = prev;
prev = prev->_parent;
}
else if (prev->_pf == 2 || prev->_pf == -2)
{
//进行旋转操作同时调节平衡因子
}
else
{
assert(false);
}
}
4.AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
1. 新节点插入较高左子树的左侧---左左:右单旋
如下:30的右节点一定是比 60小比 30大的节点,所以我们可以让60 的左节点指向30 的右节点,然后让30 来做这棵子树的根。
我们传的是平衡因子绝对值大于2的节点,也就是上图中的60位置的节点指针。
void RotetaR(Node* prev)
{
Node* left = prev->_left;
Node* parent = prev->_parent;
prev->_left = left->_right;
if (left->_right)
{
left->_right->_parent = prev;
}
left->_right = prev;
prev->_parent = left;
if (parent == nullptr)
{
_root = left;
}
else
{
if (parent->_left == prev)
{
parent->_left = left;
}
else
{
parent->_right = left;
}
}
left->_parent = parent;
left->_pf = 0;
prev->_pf = 0;
}
2. 新节点插入较高右子树的右侧---右右:左单旋
左旋与右旋类似它是让60所在位置来当子树的根节点。
void RotetaL(Node* prev)
{
Node* right = prev->_right;
Node* parent = prev->_parent;
prev->_right = right->_left;
if (right->_left)
{
right->_left->_parent = prev;
}
right->_left = prev;
prev->_parent = right;
if (parent == nullptr)
{
_root = right;
}
else
{
if (parent->_left == prev)
{
parent->_left = right;
}
else
{
parent->_right = right;
}
}
right->_parent = parent;
right->_pf = 0;
prev->_pf = 0;
}
3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋
将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再 考虑平衡因子的更新。
void RotetaLR(Node* prev)
{
Node* subL = prev->_left;
Node* subLR = subL->_right;
Node* sub = nullptr;
if (subLR->_pf == 0)
{
sub = subLR;
}
else if (subLR->_pf == 1)
{
sub = subL;
}
else
{
sub = prev;
}
RotetaL(subL);
RotetaR(prev);
if (sub == prev)
{
sub->_pf = 1;
}
else if (sub == subL)
{
sub->_pf = -1;
}
}
4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋
与3思路类似:
void RotetaRL(Node* prev)
{
Node* sub = nullptr;
Node* subR = prev->_right;
Node* subRL = subR->_left;
if (subRL->_pf == 0)
{
sub = subRL;
}
else if (subRL->_pf == 1)
{
sub = prev;
}
else
{
sub = subR;
}
RotetaR(prev->_right);
RotetaL(prev);
if (sub == prev)
{
sub->_pf = -1;
}
else if (sub == subR)
{
sub->_pf = 1;
}
}
总结: 假如以prev为根的子树不平衡,即prev的平衡因子为2或者-2,分以下情况考虑
1. prev的平衡因子为2,说明prev的右子树高,设prev的右子树的根为right
- 当right的平衡因子为1时,执行左单旋
- 当right的平衡因子为-1时,执行右左双旋
2. prev的平衡因子为-2,说明prev的左子树高,设prev的左子树的根为left
- 当left的平衡因子为-1是,执行右单旋
- 当left的平衡因子为1时,执行左右双旋
旋转完成后,原prev为根的子树个高度降低,已经平衡,不需要再向上更新。
5.AVL树的优劣
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这 样可以保证查询时高效的时间复杂度,即(logN)。
但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时, 有可能一直要让旋转持续到根的位置。
因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
感谢大当家的观看!
由于时间原因AVL树的删除并没有实现,用的是搜索二叉树的erase。
大家感兴趣的可以自己去实现一下。
因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不 错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
#include<iostream>
#include<cassert>
using namespace std;
namespace bit
{
template<class k, class v >
struct AVLNode
{
pair<k, v> _kv;
AVLNode<k, v>* _left;
AVLNode<k, v>* _right;
AVLNode<k, v>* _parent;
int _pf;
AVLNode(const pair<k, v>& key)
:_kv(key)
, _left(nullptr)
, _right(nullptr)
,_parent(nullptr)
,_pf(0)
{}
};
template<class k, class v = int>
class AVL
{
typedef AVLNode<k, v> Node;
public:
AVL() = default;
AVL(const AVL<k, v>& root)
{
_root = Copy(root._root);
}
~AVL()
{
Destroy(_root);
_root = nullptr;
}
bool Insert(const k& ret)
{
if (_root == nullptr)
{
_root = new Node({ ret,0 });
return true;
}
Node* prev = nullptr;
Node* root = _root;
while (root != nullptr)
{
if (root->_kv.first > ret)
{
prev = root;
root = root->_left;
}
else if (root->_kv.first < ret)
{
prev = root;
root = root->_right;
}
else return false;
}
Node* cur = new Node({ ret,0 });
if (prev->_kv.first > ret)
{
prev->_left = cur;
cur->_parent = prev;
}
else
{
prev->_right = cur;
cur->_parent = prev;
}
while (prev != nullptr)
{
if (cur == prev->_left)
{
prev->_pf--;
}
else
{
prev->_pf++;
}
if (prev->_pf == 0)
{
break;
}
else if (prev->_pf == -1 || prev->_pf == 1)
{
cur = prev;
prev = prev->_parent;
}
else if (prev->_pf == 2 || prev->_pf == -2)
{
if (cur == prev->_right)
{
if (cur->_pf == 1)
{
//左单旋
RotetaL(prev);
}
else
{
//右左旋
RotetaRL(prev);
}
}
if (cur == prev->_left)
{
if (cur->_pf == -1)
{
//右单选旋
RotetaR(prev);
}
else
{
//左右旋
RotetaLR(prev);
}
}
break;
}
else
{
assert(false);
}
}
return true;
}
bool Find(const k& ret)
{
Node* root = _root;
while (root != nullptr)
{
if (root->_kv.first > ret)
{
root = root->_left;
}
else if (root->_kv.first < ret)
{
root = root->_right;
}
else return true;
}
return false;
}
void Inorder()
{
inorder(_root);
cout << endl;
}
bool Erase(const k& ret)
{
Node* prev = nullptr;
Node* root = _root;
while (root != nullptr)
{
if (ret > root->_kv.first)
{
prev = root;
root = root->_right;
}
else if (ret < root->_kv.first)
{
prev = root;
root = root->_left;
}
else
{
if (root->_left == nullptr)
{
if (prev == nullptr)
{
_root = root->_right;
if(_root)
_root->_parent = nullptr;
}
else if (prev->_left == root)
{
prev->_left = root->_right;
if (root->_right)
{
root->_right->_parent = prev;
}
}
else if (prev->_right == root)
{
prev->_right = root->_right;
if (root->_right)
{
root->_right->_parent = prev;
}
}
delete root;
return true;
}
else if (root->_right == nullptr)
{
if (prev == nullptr)
{
_root = root->_left;
if(_root)
_root->_parent = nullptr;
}
else if (prev->_left == root)
{
prev->_left = root->_left;
if (root->_left)
{
root->_left->_parent = prev;
}
}
else if (prev->_right == root)
{
prev->_right = root->_left;
if (root->_left)
{
root->_left->_parent = prev;
}
}
delete root;
return true;
}
else//左右节点都不为空找右子树最小节点||左子树最大节点
{
Node* right = root->_right;
prev = root;
while (right->_left != nullptr)
{
prev = right;
right = right->_left;
}
root->_kv.first = right->_kv.first;
root->_kv.second = right->_kv.second;
if (prev == root)
{
prev->_right = right->_right;
}
else
{
prev->_left = right->_right;
}
delete right;
return true;
}
}
}
return false;
}
int _Height()
{
return height(_root);
}
private:
int height(Node* root)
{
if (root == nullptr) return 0;
int l = height(root->_left);
int r = height(root->_right);
return l > r ? l + 1 : r + 1;
}
void RotetaL(Node* prev)
{
Node* right = prev->_right;
Node* parent = prev->_parent;
prev->_right = right->_left;
if (right->_left)
{
right->_left->_parent = prev;
}
right->_left = prev;
prev->_parent = right;
if (parent == nullptr)
{
_root = right;
}
else
{
if (parent->_left == prev)
{
parent->_left = right;
}
else
{
parent->_right = right;
}
}
right->_parent = parent;
right->_pf = 0;
prev->_pf = 0;
}
void RotetaR(Node* prev)
{
Node* left = prev->_left;
Node* parent = prev->_parent;
prev->_left = left->_right;
if (left->_right)
{
left->_right->_parent = prev;
}
left->_right = prev;
prev->_parent = left;
if (parent == nullptr)
{
_root = left;
}
else
{
if (parent->_left == prev)
{
parent->_left = left;
}
else
{
parent->_right = left;
}
}
left->_parent = parent;
left->_pf = 0;
prev->_pf = 0;
}
void RotetaLR(Node* prev)
{
Node* subL = prev->_left;
Node* subLR = subL->_right;
Node* sub = nullptr;
if (subLR->_pf == 0)
{
sub = subLR;
}
else if (subLR->_pf == 1)
{
sub = subL;
}
else
{
sub = prev;
}
RotetaL(subL);
RotetaR(prev);
if (sub == prev)
{
sub->_pf = 1;
}
else if (sub == subL)
{
sub->_pf = -1;
}
}
void RotetaRL(Node* prev)
{
Node* sub = nullptr;
Node* subR = prev->_right;
Node* subRL = subR->_left;
if (subRL->_pf == 0)
{
sub = subRL;
}
else if (subRL->_pf == 1)
{
sub = prev;
}
else
{
sub = subR;
}
RotetaR(prev->_right);
RotetaL(prev);
if (sub == prev)
{
sub->_pf = -1;
}
else if (sub == subR)
{
sub->_pf = 1;
}
}
void inorder(Node* root)
{
if (root == nullptr) return;
inorder(root->_left);
cout << root->_kv.first << ' ';
inorder(root->_right);
}
Node* Copy(const Node* root)
{
if (root == nullptr) return nullptr;
Node* val = new Node(root->_kv);
val->_left = Copy(root->_left);
val->_right = Copy(root->_right);
return val;
}
void Destroy(Node* root)
{
if (root == nullptr) return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
Node* _root = nullptr;
};
}