一、什么是AVL树
二叉树搜索树存在缺点,就是二叉搜索树如果不平衡左子树和右子树高度相差较多时,搜索的效率会降低。甚至可能会退化成链表。就如同下图。
这时候我们可以加入一个平衡因子,来解决这个问题
平衡因子 = 右子树的高度 - 左子树的高度,且平衡因子的绝对值不超过1.这个就是AVL树,也叫二叉搜索平衡树。
二、AVL树的框架
这是AVL树的基本框架
#include<iostream>
using namespace std;
template<class T>
struct AVLTreeNode
{
T _key;
AVLTreeNode<T>* _left;
AVLTreeNode<T>* _right;
AVLTreeNode<T>* _parent;
AVLTreeNode(const T& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
{}
};
template<class T>
class AVLTree
{
typedef AVLTreeNode<T> Node;
public:
private:
Node* _root = nullptr;
int _bf = 0;
};
这里使用struct来定义AVL树的节点是因为struct默认是public,方便后面的使用。
三、AVL树的插入
因为我们的AVL树本质上也是一颗二叉搜索树,所以我们插入的过程,首先也是和二叉搜索树的插入过程一样,先循环比较找到要插入的位置。
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key == key)
{
return false;
}
else
{
break;
}
}
cur = new Node(key);
if (cur->_key > parent->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
.......
然后这时候我们就要考虑平衡因子的问题,我们会遇到以下几种情况。
插入的节点的平衡因子肯定是0,然后我们再根据插入的节点在父节点的左边还是右边,如果在左边,bf--,如果在右边,bf++。
1.如果插入后父节点的平衡因子为0,则说明这个节点平衡,不需要调整。
插入在根节点的右边
父节点的bf++,变成0,则不需要再做处理。
2.如果父节点的平衡因子是1或者-1,说明肯定是有一边变高了,就会影响到祖先节点,这时候我们就需要继续向上处理。
就比如
我们插入前
插入一个节点在最右节点的右边
cur在paren的右边,则parent的bf++,变成1.
然后再看parent的parent,也就是根节点,对于根节点来说,右子树增高了,
所以bf++,变成1.
3,插入后parent的bf为2或者-2,
这时候我们就需要通过旋转来调整,来让bf为0。
就比如我们插入后,调整bf到p的位置
我们的右子树高,所以要通过左旋来调整。
左旋的核心步骤是
p -> _right = cur->_left
c->_left = p;
然后在调节一下我们的父子节点关系和bf即可。
右旋也是一样。
4.右左旋
右左旋是当我们的parentbf为2,cur的bf为-1,是个折线的时候,就比如
先将cur右旋,再将parent左旋。
然后再调节平衡因子。我们要根据插入的节点的父节点情况来调节平衡因子。也就是根据图上的根节点它插入后没旋转之前的情况来调节。
左右双旋和右左双旋思路也是一样的。
这是整个插入过程的代码
bool Insert(const pair<K,V>& val)
{
if (_root == nullptr)
{
_root = new Node(val);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_val.first > val.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_val.first < val.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_val.first == val.first)
{
return false;
}
else
{
break;
}
}
cur = new Node(val);
if (cur->_val.first > parent->_val.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
while (parent)
{
//插入的节点在父节点的左边,bf--
if (parent->_left == cur)
{
parent->_bf--;
}
//插入的节点在父节点的右边,bf++
else
{
parent->_bf++;
}
//如果父节点的bf的绝对值为1,则不需要旋转,向上调节节点。
if (parent->_bf == -1 || parent->_bf == 1)
{
cur = parent;
parent = parent->_parent;
}
//如果父节点的bf为0,则不需要向上调整
else if (parent->_bf == 0)
{
break;
}
//如果父节点的bf为2或-2,则需要旋转
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1)
{
RotationL(parent);
break;
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotationR(parent);
break;
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotationRL(parent);
break;
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotationLR(parent);
break;
}
}
else
{
assert(false);
}
}
return true;
}
void RotationL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
curleft->_parent = parent;
Node* ppnode = parent->_parent;
cur->_left = parent;
parent->_parent = cur;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
parent->_bf = cur->_bf = 0;
}
void RotationR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
Node* ppnode = parent->_parent;
//开始右旋,将cur的右接parent,cur的右节点接到parent的左
cur->_right = parent;
parent->_left = curright;
//如果parent是root,就让cur变成root
if (parent == _root)
{
cur = _root;
cur->_parent = nullptr;
}
//处理parent
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode;
}
parent->_parent = cur;
if(curright)
curright->_parent = parent;
cur->_bf = parent->_bf = 0;
}
void RotationRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
//先把bf存起来,因为调用左旋和右旋函数会把bf给修改成0
//curbf是一会判断ppnode和parent的bf为什么的因素
int curleftbf = curleft->_bf;
RotationR(cur);
RotationL(parent);
if (curleftbf == 0)
{
cur->_bf = curleft->_bf = parent->_bf = 0;
}
else if (curleftbf == 1)
{
curleft->_bf = cur->_bf = 0;
parent->_bf = -1;
}
else if (curleftbf == -1)
{
curleft->_bf = parent->_bf = 0;
cur->_bf = 1;
}
else
{
assert(false);
}
}
void RotationLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int currightbf = curright->_bf;
//左右旋
RotationL(cur);
RotationR(parent);
if (currightbf == 0)
{
cur->_bf = parent->_bf = curright->_bf = 0;
}
else if (currightbf == 1)
{
curright->_bf = parent->_bf = 0;
cur->_bf = -1;
}
else if (currightbf == -1)
{
curright->_bf = cur->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}