AVL树,又名高度平衡搜索树,他会一直保证树的左右子树高度不超过1,一旦超过,就会进行调整,本篇博客采用平衡因子法,主要来讲如何用平衡因子控制平衡,以及AVL树的代码实现
首先介绍一下平衡因子,他存在于每个节点当中,当高度平衡时,平衡因子为0,左子树高1,那么平衡因子-1,右子树高1,平衡因子+1,例如图中这棵树
root的左子树比右子树高,所以平衡因子为-1,而left左右为空树,是平衡状态,那么平衡因子就为0。
介绍完了平衡因子调节规则,我们就来说一下AVL树的插入如何实现,首先还是按照搜索树的方式进行插入,新节点与树中节点比较key值,找到新节点该去的位置,创建新节点,并与父树连接,要提一点的是,相比于搜索树,AVL树节点中还要使用parents用来帮助AVL树的实现,连接之后,就要开始最复杂的一部分,即更新平衡因子,新节点无需更新,他一定是平衡的,所以要从新节点的parents进行更新,首先要进行判断,如果新节点为右,那么parents的平衡因子++,为左,平衡因子--,如果++或--后平衡因子为零,那么代表parents的左右树已经平衡,无需向上更新,如果为1,或者-1,那么代表parents父树的高度也已经改变,需要向上更新,即让parents = parents->parents,继续向上更新,同时再将一个变量将parents赋值之前的值进行记录,再进行比较,先看一下这部分的代码
bool insert(const pair<key,value> kv)
{
//找到新节点该插入的位置
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parents = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parents = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parents = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//创建新节点并连接
cur = new Node(kv);
if (parents->_kv.first > cur->_kv.first)
{
parents->_left = cur;
cur->_parents = parents;
}
else
{
parents->_right = cur;
cur->_parents = parents;
}
//更新平衡因子
while (parents)
{
if (parents->_right == cur)
{
parents->_bf++;
}
else if(parents->_left == cur)
{
parents->_bf--;
}
if (parents->_bf == 0)
{
break;
}
else if (parents->_bf == -1 || parents->_bf == 1)
{
cur = parents;
parents = parents->_parents;
}
}
return true;
}
之后就到了AVL树的关键点,如果平衡因子等于2或-2,就代表需要进行旋转 ,我们先从一张图里看一下如何旋转的
这是一个左旋转 ,将cur的左给parents的右,parents成为cur的左,旋转完成后再更新cur和parents的parents指针指向,下面是左旋转代码
void Left_Rotate(Node* parents)
{
Node* subR = parents->_right;
Node* subRL = subR->_left;
subR->_left = parents;
parents->_right = subRL;
//旋转时如果subRL为空,则不需要parents
if(subRL)
subRL->_parents = parents;
Node* ppnode = parents->_parents;
parents->_parents = subR;
//parents->parents为空,说明parents为root
if (_root == parents)
{
_root = subR;
subR->_parents = nullptr;
}
else
{
if (ppnode->_left == parents)
ppnode->_left = subR;
else
ppnode->_right = subR;
subR->_parents = ppnode;
}
parents->_bf = subR->_bf = 0;
}
下面我们想一下,什么情况下触发左旋转呢,从之前图中我们可以看到,parents平衡因子为2,cur为1,触发了左旋,同样,如果parents平衡因子为-2,cur为-1,那么会触发右旋,旋转逻辑与右转类似,将cur的右给parents的左,parents成为cur的右,下图是左旋图和右旋代码
void Right_Rotate(Node* parents)
{
Node* subL = parents->_left;
Node* subLR = subL->_right;
subL->_right = parents;
parents->_left = subLR;
if (subLR)
subLR->_parents = parents;
Node* ppnode = parents->_parents;
parents->_parents = subL;
if (_root == parents)
{
_root = subL;
subL->_parents = nullptr;
}
else
{
if (ppnode->_left == parents)
ppnode->_left = subL;
else
ppnode->_right = subL;
subL->_parents = ppnode;
}
parents->_bf = subL->_bf = 0;
}
可能有朋友看到这会很疑惑,AVL树貌似也很简单嘛,看不出什么难度,那么下面硬菜来了,如果parents为-2,而cur为1呢,如下图一样
那么这时我们好像进行左旋和右旋都无法解决,此时就需要进行两次旋转,先从cur开始旋转,将
newnode的左给cur的右,cur成为newnode的左,也就是先进行了一次左旋,旋转后如图
再对parents进行一次右旋,将newnode的右给parents的左,parents成为newnode的右,旋转后如图:
先左后右的旋转我们称为左右双旋,完整旋转图如下:
这是当parents为-2,cur为1的左右双旋,同样,当parents为2,cur为-1时,要进行右左双旋,我就不一一展示了,直接放完整旋转图,如下
可能有朋友还是觉得不难,不就是旋转了两次嘛,看起来也很简单,下面就要上AVL树的搬砖了,看各位能不能啃动。
其实逻辑上理解AVL树的旋转的确不算很难,最让初学者头疼的其实是旋转后AVL树的平衡因子调节,无论是左旋还是右旋,只需将旋转的两个节点的平衡因子置为零即可,因为左右旋转后他们两个都平衡了,重点在双旋的平衡因子调节 ,我们先来看左右双旋的平衡因子调节
此时是parents的平衡因子为-2,subL的平衡因子为1,subLR的平衡因子为-1,我们还是要先进行双旋,先将subL进行左旋转,subLR的左给subL的右,subL成为subLR的左,再对parents进行右旋转,旋转图如下
可以看到,旋转完后,parents的平衡因子为1,subL和subLR皆为0
如果subLR的平衡因子一开始为1,下图为subLR的平衡因子为1的旋转过程
旋转完后,subL的平衡因子应该是-1,parents和subLR为0
左右双旋算上一开始讲解的那种情况,总共为三种可能,分别是subLR的平衡因子为1,-1,和0时的不同情况,下面是左右双旋的代码,可以配合图食用更佳
Node* subL = parents->_left;
Node* subLR = subL->_right;
int bf = 0;
if (subLR)
bf = subLR->_bf;
Left_Rotate(parents->_left);
Right_Rotate(parents);
if (bf == -1)
{
parents->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else if(bf == 1)
{
parents->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if(bf == 0)
{
parents->_bf = 0;
subL->_bf = 0;
}
else
assert(false);
}
右左双旋的逻辑与左右双旋类似,我们直接看suRL分别为1和-1时的两幅旋转图
从图中我们可以看到,当subLR为1时,旋转之后parents为-1,其余为0,档subLR为-1时,旋转之后subL为-1,那么右左双旋代码如下
void right_left_Rotate(Node* parents)
{
//先旋转
Node* subR = parents->_right;
Node* subRL = subR->_left;
int bf = 0;
if (subRL)
bf = subRL->_bf;
Right_Rotate(parents->_right);
Left_Rotate(parents);
//更新平衡因子
if (bf == 1)
{
parents->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
parents->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
parents->_bf = 0;
subR->_bf = 0;
}
else
assert(false);
}
下面是insert的完整代码
bool insert(const pair<key,value> kv)
{
//找到新节点该插入的位置
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parents = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parents = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parents = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//创建新节点并连接
cur = new Node(kv);
if (parents->_kv.first > cur->_kv.first)
{
parents->_left = cur;
cur->_parents = parents;
}
else
{
parents->_right = cur;
cur->_parents = parents;
}
//更新平衡因子
while (parents)
{
if (parents->_right == cur)
{
parents->_bf++;
}
else if(parents->_left == cur)
{
parents->_bf--;
}
if (parents->_bf == 0)
{
/* cur = parents;
parents = parents->_parents;*/
break;
}
else if (parents->_bf == -1 || parents->_bf == 1)
{
cur = parents;
parents = parents->_parents;
}
//走到这说明树的平衡性打破,需要旋转
else if (parents->_bf == 2 || parents->_bf== -2)
{
if (parents->_bf == 2)
{
if (cur->_bf == 1)
{
Left_Rotate(parents);
}
else if (cur->_bf == -1)
{
right_left_Rotate(parents);
}
}
else if (parents->_bf == -2)
{
if (cur->_bf == 1)
{
left_right_Rotate(parents);
}
else if (cur->_bf == -1)
{
Right_Rotate(parents);
}
}
break;
}
}
return true;
}
insert里相应要进行旋转的地方在前面图中我们已经可以清晰的看出来了,最后是我对AVL测试的所有代码
#pragma once
#include<utility>
#include<iostream>
#include<assert.h>
using std::make_pair;
using std::pair;
using std::cout;
using std::endl;
namespace sxh_AVLTree
{
template<typename key,typename value>
struct _AVLTreeNode
{
_AVLTreeNode<key,value>* _left;
_AVLTreeNode<key, value>* _right;
_AVLTreeNode<key, value>* _parents;
int _bf;
pair<key, value> _kv;
_AVLTreeNode(const pair<key,value> kv):_kv(kv),_bf(0),_left(nullptr),_right(nullptr),_parents(nullptr){}
};
template<typename key,typename value>
class AVLTree
{
typedef _AVLTreeNode<key,value> Node;
public:
Node* _root;
public:
AVLTree(const pair<key, value> kv) :_root(new Node(kv)) {}
bool insert(const pair<key,value> kv)
{
//找到新节点该插入的位置
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parents = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parents = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parents = cur;
cur = cur->_right;
}
else
{
return false;
}
}
//创建新节点并连接
cur = new Node(kv);
if (parents->_kv.first > cur->_kv.first)
{
parents->_left = cur;
cur->_parents = parents;
}
else
{
parents->_right = cur;
cur->_parents = parents;
}
//更新平衡因子
while (parents)
{
if (parents->_right == cur)
{
parents->_bf++;
}
else if(parents->_left == cur)
{
parents->_bf--;
}
if (parents->_bf == 0)
{
/* cur = parents;
parents = parents->_parents;*/
break;
}
else if (parents->_bf == -1 || parents->_bf == 1)
{
cur = parents;
parents = parents->_parents;
}
//走到这说明树的平衡性打破,需要旋转
else if (parents->_bf == 2 || parents->_bf== -2)
{
if (parents->_bf == 2)
{
if (cur->_bf == 1)
{
Left_Rotate(parents);
}
else if (cur->_bf == -1)
{
right_left_Rotate(parents);
}
}
else if (parents->_bf == -2)
{
if (cur->_bf == 1)
{
left_right_Rotate(parents);
}
else if (cur->_bf == -1)
{
Right_Rotate(parents);
}
}
break;
}
}
return true;
}
void Left_Rotate(Node* parents)
{
Node* subR = parents->_right;
Node* subRL = subR->_left;
subR->_left = parents;
parents->_right = subRL;
//旋转时如果subRL为空,则不需要parents
if(subRL)
subRL->_parents = parents;
Node* ppnode = parents->_parents;
parents->_parents = subR;
//parents->parents为空,说明parents为root
if (_root == parents)
{
_root = subR;
subR->_parents = nullptr;
}
else
{
if (ppnode->_left == parents)
ppnode->_left = subR;
else
ppnode->_right = subR;
subR->_parents = ppnode;
}
parents->_bf = subR->_bf = 0;
}
void Right_Rotate(Node* parents)
{
Node* subL = parents->_left;
Node* subLR = subL->_right;
subL->_right = parents;
parents->_left = subLR;
if (subLR)
subLR->_parents = parents;
Node* ppnode = parents->_parents;
parents->_parents = subL;
if (_root == parents)
{
_root = subL;
subL->_parents = nullptr;
}
else
{
if (ppnode->_left == parents)
ppnode->_left = subL;
else
ppnode->_right = subL;
subL->_parents = ppnode;
}
parents->_bf = subL->_bf = 0;
}
void right_left_Rotate(Node* parents)
{
//先旋转
Node* subR = parents->_right;
Node* subRL = subR->_left;
int bf = 0;
if (subRL)
bf = subRL->_bf;
Right_Rotate(parents->_right);
Left_Rotate(parents);
//更新平衡因子
if (bf == 1)
{
parents->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
parents->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
parents->_bf = 0;
subR->_bf = 0;
}
else
assert(false);
}
void left_right_Rotate(Node* parents)
{
Node* subL = parents->_left;
Node* subLR = subL->_right;
int bf = 0;
if (subLR)
bf = subLR->_bf;
Left_Rotate(parents->_left);
Right_Rotate(parents);
if (bf == -1)
{
parents->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else if(bf == 1)
{
parents->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if(bf == 0)
{
parents->_bf = 0;
subL->_bf = 0;
}
else
assert(false);
}
void __InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
__InOrder(root->_left);
std::cout << root->_kv.first<< ":" << root->_kv.second << endl;
__InOrder(root->_right);
}
void InOrder()
{
__InOrder(this->_root);
std::cout << std::endl;
}
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;
}
bool _Isbalance(Node* root)
{
if (root == nullptr)
return true;
int LeftHeight = Height(root->_left);
int RightHeight = Height(root->_right);
if (RightHeight - LeftHeight!= root->_bf)
{
cout << "平衡因子异常" << root->_bf << endl;
cout << "rightheight - leftheight = " << RightHeight - LeftHeight << endl;
return false;
}
return abs(RightHeight - LeftHeight) < 2
&& _Isbalance(root->_left)
&& _Isbalance(root->_right);
}
bool Isbalance()
{
return _Isbalance(this->_root);
}
};
}
有人看的话我会来补一下删除的逻辑讲解,没人看就不写了,写这篇博客已经一个多小时了,find依旧是搜索树的find,这个相信要学AVL树的各位都会写