目录
一、AVL树介绍
学习过二叉搜索树后,虽然它可以缩短查找的效率,但当数据有序或接近有序时,二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,因此AVL树就诞生了。
AVL树可以是空树,也可以是具有以下性质的搜索二叉树:
- 左右子树都是AVL树
- 左右子树高度之差(平衡因子)的绝对值不超过1(可以是0、-1、1)
这样设计可以降低树的高度,从而减少平均搜索长度。
二、AVL树的实现
2.1 节点的定义
struct AVLTreeNode
{
T _data;
int _bf;
AVLTreeNode<T>* _left;
AVLTreeNode<T>* _right;
AVLTreeNode<T>* _parent;
AVLTreeNode(const T& data)
:_data(data) //节点的值
,_left(nullptr) //节点的左孩子
,_right(nullptr) //节点的右孩子
,_parent(nullptr) //节点的父亲
,_bf(0) //节点的平衡因子
{}
};
2.2 节点的插入
在插入时,我们要不断更新平衡因子,才能根据平衡因子来检测AVL树的平衡性。
平衡因子的计算方式是右子树-左子树,在我们插入一个节点后,显然它的parent的平衡因子会改变,根据计算方式可以知道:
- 新增在左,parent平衡因子减1
- 新增在右,parent平衡因子加1
根据更新后的平衡因子我们要怎么做出相应的动作而维持平衡性呢?
- 更新后parent的平衡因子 == 0,说明parent所在子树的高度不变,不会再影响祖先,不用再继续沿着root的路径往上更新
- 更新后parent的平衡因子 == 1 或 -1,说明parent所在子树的高度发生变化,会再影响祖先,因此需要再继续沿着root的路径往上更新
- 更新后parent的平衡因子 == 2 或 -2,说明parent所在子树的高度发生变化并且不平衡了,接下来就需要对其所在的子树进行旋转,让他平衡。
2.3 AVL树的旋转
在旋转时,我们要注意:
- 保证它还是搜索树
- 将其旋转为平衡树并且降低这个子树的高度
在旋转时一共有四种情况:
2.3.1 左旋
如上图,当 parent->_bf == 2 && cur->_bf == 1 时,我们对parent进行左单旋,可以理解为把cur往上提,把cur的左边给parent的右边,把parent给cur的左边。
来看下代码:
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft; //cur的左给parent的右
if (curleft)
{
curleft->_parent = parent; //cur的左向上找自己的_parent:parent
}
cur->_left = parent; //parent给cur的左
Node* ppnode = parent->_parent; //ppnode记录旋转前parent的_parent
parent->_parent = cur; //parent向上找旋转后自己的_parent:cur
if (parent == _root) //如果parent就是根节点,旋转后cur成为根
{
_root = cur;
cur->_parent = nullptr;
}
else //如果不是根节点,则看原来parent的位置,将cur对其
{ //位置替换
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
cur->_parent = ppnode; //更新cur的_parent
}
parent->_bf = cur->_bf = 0; //旋转后平衡因子均为0
}
2.3.2 右旋
如上图,当 parent->_bf == -2 && cur->_bf == -1 时,我们对parent进行右单旋,可以理解为把cur往上提,把cur的右边给parent的左边,把parent给cur的右边。
代码思想与左旋是一样的:
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
Node* ppnode = parent->_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;
}
2.3.3 右左旋
当遇到有拐点的情况时,我们就要进行双旋:如上图,先对cur进行右旋,再对parent进行左旋
旋转我们可以复用上面写过的代码,不过这里更新平衡因子要发生变化: 这里平衡因子受到最下面节点(我们称为curleft)平衡因子的影响:
- 如果curleft的平衡因子为0,那么它只能是没有孩子的节点,那么最终旋转到上面这三个节点的平衡因子都是为0的
- 如果curleft的平衡因子为1,如下图,可以看出旋转后parent的平衡因子变为-1,其余的都为0
3. 如果curleft的平衡因子为 -1,如下图,可以看出旋转后cur的平衡因子变为1,其余的都为0
因此我们可以写出代码:
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(cur);
RotateL(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
2.3.4 左右旋
如上图,它与右左旋相反,但是大体的思路是一样的,旋转后根据curright的平衡因子来调整平衡因子,分析的步骤是一样的,这里就不再介绍,直接来看代码:
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(cur);
RotateR(parent);
if (bf == 0)
{
cur->_bf = 0;
curright->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = -1;
curright->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
curright->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}
2.4 总体代码实现
经过上面的分析,我们就来看看完整的代码吧:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class T>
struct AVLTreeNode
{
T _data;
int _bf;
AVLTreeNode<T>* _left;
AVLTreeNode<T>* _right;
AVLTreeNode<T>* _parent;
AVLTreeNode(const T& data)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<class T>
class AVLTree
{
typedef AVLTreeNode<T> Node;
public:
bool insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data < data)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_data > data)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(data);
if (parent->_data < data)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//插入后更新平衡因子(=右子树高度-左子树高度)
while (parent)
{
if (cur == parent->_left)
{
parent->_bf--;
}
else
{
parent->_bf++;
}
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
//继续向上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
//不满足AVL,需要翻转
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)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
break;
}
else
{
assert(false);
}
}
return true;
}
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
Node* ppnode = parent->_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 RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
Node* ppnode = parent->_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 RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(cur);
RotateL(parent);
if (bf == 0)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
cur->_bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
}
else
{
0 assert(false);
}
}
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(cur);
RotateR(parent);
if (bf == 0)
{
cur->_bf = 0;
curright->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
cur->_bf = -1;
curright->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
cur->_bf = 0;
curright->_bf = 0;
parent->_bf = 1;
}
else
{
assert(false);
}
}
//检验AVL
bool isAVLTree()
{
return isAVLTree(_root);
}
bool isAVLTree(Node* root)
{
if (root == nullptr)
{
return true;
}
int leftHeight = height(root->_left);
int rightHeight = height(root->_right);
if (root->_bf != (rightHeight - leftHeight))
{
cout << root->_data << "平衡因子出错" << root->_bf << endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& isAVLTree(root->_right)
&& isAVLTree(root->_left);
}
int height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int leftHeight = height(root->_left);
int rightHeight = height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
private:
Node* _root = nullptr;
};