AVL树是在二叉搜索树的基础上的一种优化, 在操作二叉搜索树的时候无论是插入节点还是删除节点,都需要去遍历寻找位置,因此操作二叉搜索树的时间复杂度为O(log N),但是当我们的二叉搜索树为单边树的时候, 进行一次操作的时间复杂度就变成了O(N),这两者之间的差距还是非常大的.
AVL树是二叉搜索树的基础之上多了一个平衡因子, 每个节点的平衡因子就是其对应的结点的右子树的高度与左子树的高度之差, 当这个平衡因子等于2,或者是等于-2的时候,就对这棵子树进行旋转调整,使他的平衡因子达到平衡
AVL树的结点定义
template<class T>
struct AVLTreeNode{
AVLTreeNod(const T& x)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_data(x)
,_bf(0)
{}
AVLTreeNode<T>* _left;
AVLTreeNode<T>* _right;
AVLTreeNode<T>* _parent;
T _data;
int _bf;
};
AVL树的插入
在插入新节点的时候, 大致的可以分为两步,一是创建新节点插入,二是平衡因子的调整
由于插入新节点的时候. 会使这个节点以上所有的父路径上的几点的高度加一, 因此有可能所有父路径上的结点的平衡因子都会改变,所以要利用循环一直向上调整直到平衡为止.
-
List item当父亲节点的平衡因子为0时, 达到平衡,直接退出循环(注: 当父亲结点为0时,此时这个子树的高度和插入新节点之前的高度是一样的,所以不影响以上的结点,因此退出循环)
-
当父亲结点的平衡因子为1或者-1时,要跟新当前的结点和父亲结点,实现向上的循调整
-
当父亲结点的平衡因子为2或者-2的时候, 就要进行旋转来调节平衡因子这里又分几种情况.
(1).当父亲的平衡因子为-2,当前结点的平衡因子为-1是,为下图的情况,需要进 行一次向右旋转(2).当父亲的平衡因子为2,当前节点的平衡因子为1时,为下图情况,需要进行一次向左旋转
(3).当父亲的平衡因子为2,当前节点的平衡因子为-1时,为下图情况,需要先进行一次向右旋转在进行一次向左旋转
(4).当父亲的平衡因子为-2,当前节点的平衡因子为1时,为下图情况,需要先进行一次向左旋转在进行一次向右旋转
左旋与右旋
左旋和右旋的代码基本上是相似的, 有上图可知,改变的指针一共有6个,以左旋为例: 定义节点subR为父亲结点的右孩子,定义结点subRL为subR节点的左孩子,还需要定义Parent节点为父亲节点的父亲,其中需要数注意subRL可能为空时会造成空指针的解引用,因此在对subRL节点进行向上链接新的附近节点是,要进行判断是否为空,具体代码如下
bool Insert(const T& x){
//树为空时插入到根节点上
if (_root == nullptr){
_root = new Node(x);
return true;
}
//1.在二叉搜索树中遍历寻找到节点的插入位置
pNode cur = _root;
pNode parent = nullptr;
while (cur){
if (x < cur->_data){
parent = cur;
cur = cur->_left;
}
else if(x>cur->_data){
parent = cur;
cur = cur->_right;
}
else
return false;
}
//2.创建节点插入
cur = new Node(x);
if (cur->_data > parent->_data)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
//3.对平衡因子进行调整
while (parent){
//(1).更新插入节点的父亲结点的平衡因子
if (parent->_left == cur)
--parent->_bf;
else
++parent->_bf;
//(2).判断当前节点的平衡因子
if (parent->_bf == 0)
break;
//父亲节点的平衡因子为1或者-1的时候继续向上调整
else if (parent->_bf == 1 || parent->_bf == -1){
cur = parent;
parent = cur->_parent;
}
//父亲节点的平衡因子为2或者-2的时候要进行旋转 (分情况进行分析)
else if (parent->_bf == 2 || parent->_bf == -2){
//A情况,只需进行一次向右单旋
if (parent->_bf == -2 && cur->_bf == -1)
RotateR(parent);
//B情况,只需进行一次向左单旋
else if (parent->_bf == 2 && cur->_bf == 1)
RotateL(parent);
//C情况,先进行一次左旋,在进行一次右旋
else if (parent->_bf == -2 && cur->_bf == 1){
pNode subL = parent->_left;
pNode subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1){
subL->_bf = -1;
parent->_bf = 0;
}
else if (bf = -1){
subL->_bf = 0;
parent->_bf = 1;
}
}
//D情况,先进行一次右旋,在进行一次左旋
else if (parent->_bf == 2 && cur->_bf == -1){
pNode subR = parent->_right;
pNode subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == -1){
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 1){
subR->_bf = 0;
parent->_bf = -1;
}
}
break;
}
}
return true;
}
//右旋
void RotateR(pNode cur){
pNode sub = cur;
pNode subL = cur->_left;
pNode subLR = subL->_right;
subL->_right = sub;
sub->_left = subLR;
if (subLR)
subLR->_parent = sub;
if (cur != _root){
pNode parent = cur->_parent;
if (parent->_left == cur)
parent->_left = subL;
else
parent->_right = subL;
subL->_parent = parent;
}
else{
_root = subL;
subL->_parent = nullptr;
}
sub->_parent = subL;
subL->_bf = sub->_bf = 0;
}
//左旋
void RotateL(pNode cur){
pNode sub = cur;
pNode subR = cur->_right;
pNode subRL = subR->_left;
subR->_left = sub;
sub->_right = subRL;
if (subRL)
subRL->_parent = sub;
if (cur != _root){
pNode parent = cur->_parent;
if (parent->_left == cur)
parent->_left = subR;
else
parent->_right = subR;
subR->_parent = parent;
}
else{
_root = subR;
subR->_parent = nullptr;
}
sub->_parent = subR;
subR->_bf = sub->_bf = 0;
}