前言:
目录
(一)AVL树的概念
(二)AVL树的模拟实现
(1)AVL树结点的定义
(2)AVL树部分功能的实现
1、查找
2、插入(重点!)
2.1插入结点后平衡因子的变化
2.2情况分析
2.3旋转操作的实现
2.3.1左单旋
2.3.2右单旋
2.3.3左右双旋
2.3.4右左双旋
(三) 验证AVL树
1、中序遍历
2、判断树是否平衡
(四)总代码详解
首先AVL树是一棵二叉搜索树,一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
图示:
图中结点外数字代表平衡因子→右子树高度-左子树高度
AVL树又叫高度平衡二叉搜索树。
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 〇(logN)搜索时间复杂度 〇(logN)。
~直接实现key_value的结构 – 三叉链的形式(带父节点)
图示如下:
其中:
_bf-----balance factor,代表平衡因子
右子树与左子树的高度差
和二叉搜索树查找功能的实现几乎一致:
从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。最多查找高度次,走到到空,还没找到,这个值不存在。
这里链接是比较容易的,但是链接之后对各个结点中的平衡因子的调整则是比较费劲。
所以重点就在于分析各种可能出现平衡因子的情况,然后调整树来解决。
1.首先我们考察插入结点和它父亲结点之间的关系变化:
2.然后我们再以此为基础考察其他结点的变化情况:
我们以上图为例,8结点作为父亲结点,平衡因子发生了变化,这就有可能继续导致8结点的父亲结点的平衡因子发生变化。
简而言之,插入一个结点真正会影响的是其祖先的平衡因子的改变。
3.处理方法
(1)向上更新:
(2)如何向上更新:
具体过程如下:
- 子树高度变了,就要继续往上更新
- 子树的高度不变, 则更新完成
- 子树违反平衡规则,则停止更新, 旋转子树
2.父亲结点平衡因子是1或者-1时继续向上更新:
3.更新过程中,父亲结点平衡因子出现了2或者-2,说明子树不平衡了,需要上述说到的旋转处理(下文中会详解旋转的几种情况)
看了如上的代码有点抽象,我们下面用具体例子来讲解此情况下不同的情景:
我们也可以明晰的看出上述代码有四个条件语句,每个if其实代表的就是一种情景,我们详细分析:
情景一:
这时我们发现8作为父亲结点时出现了平衡因子为2的情况,此时cur结点平衡因子为1
情景二:
这时我们发现8作为父亲结点时出现了平衡因子为2的情况,此时cur结点平衡因子为-1
情景三,情景四大家可以自己画图啦!其实就是cur所在子树变为parent左子树的一些情况,下面详解旋转操作中将会对这几种情景解释并解决!
4.一定要检查 -- 不保证其他地方不会出现错误
比如插入之前AVL数就存在平衡子树,|平衡因子| >= 2结点。
上述我们已经阐述了,在什么情况下需要对AVL树进行旋转操,接下来我们就来讲一下具体的旋转步骤。
旋转原则:
旋转一共分为四种旋转方式:
当右子树高的时候,这时就要向左旋转。
旋转过程:
图示:
代码图解:(后续代码统一给)
有左单旋的基础,我们知道右单旋的情况:
当左子树高的时候,这时就要向右旋转。
旋转过程:
图示:
代码图解:
在一些情况中,左单旋和右单旋是无法解决问题的,需要二次旋转来降低子树高度,
比如下面的情况:
这时候无论怎么单旋,我们都无法降低子树的高度。
所以这里我们要用到双旋。
这样一来,我们就把单次旋转无法降低高度的情况解决了!
详细过程可以跟着图示来理解。
代码图解:
这里主要是根据subLR平衡因子的不同情况来给降低高度后子树中不同节点的平衡因子赋值,大家可以模仿上面的图示把不同情况画下来!
两种解决方案:
我们采用第一种方法,单独将平衡因子拿出来处理。
和上面的左右双旋的情况类似:
图示:
上面我们基本完成了AVL树的两大重要的功能实现(删除操作有困难,本文暂不实现,后续深入会讨论),下面我们要验证我们写的树是不是AVL树。
主要验证两大方面:
介绍前,我们先说明一点:
多套一层,这样就可以符合我们使用这些接口函数的习惯了!
其实通过上面的讲解后,中序遍历作者已经给出啦:
我们选择和之前中序历遍搜索二叉树一样的方法,采用递归的方式解决。
我们一同思考,如何判断左右子树高度差在2以内呢?
求树的高度:
其实我们之前也讲解过,采用递归的方法更容易来求解。
思路:树的高度就是左子树或者右子树高的那一颗树的高度加上根节点高度(也就是+1)
判断树是否平衡:
我们求解左右子树的高度后记录下来,相减的绝对值看是否是二以内。
当然,对于树中每一个结点我们都要判断他的子树是否符合条件,所以也要用到递归:
#include
#include
#include
using namespace std;
template
struct AVLTreeNode
{
AVLTreeNode* _left;
AVLTreeNode* _right;
AVLTreeNode* _parent;
pair _kv;
int _bf;//平衡因子
AVLTreeNode(const pair& kv)
:
_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv),
_bf(0)
{}
};
template
class AVLTree
{
typedef AVLTreeNode Node;
public:
bool Insert(const pair& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//更新平衡因子
while (parent)
{
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if (parent->_bf == 1 || parent->_bf == -1)
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
//子树不平衡了 -- 需要旋转处理(左单旋的特征 -- 右边高)
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)//左右双旋
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)//右左双旋
{
RotateRL(parent);
}
//旋转完之后ppNode为根的子树高度不变 -- 所以对ppNode的平衡因子没有影响
break;
}
else // 一定要检查 -- 不保证其他地方不会出现错误
{
//插入之前AVL数就存在平衡子树,|平衡因子| >= 2结点
assert(false);
}
}
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
return _IsBalance(_root);
}
int Height()
{
return _Height(_root);
}
private:
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int HeightL = _Height(root->_left);
int HeightR = _Height(root->_right);
if (HeightR - HeightL != root->_bf)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return abs(HeightL - HeightR) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
空树也是AVL树
//if (nullptr == root)
// return true;
计算pRoot节点的平衡因子:即pRoot左右子树的高度差
//int leftHeight = _Height(root->_left);
//int rightHeight = _Height(root->_right);
求差值
//int diff = rightHeight - leftHeight;
如果计算出的平衡因子与pRoot的平衡因子不相等,或者
pRoot平衡因子的绝对值超过1,则一定不是AVL树
//if (abs(diff) >= 2)
//{
// cout << root->_kv.first << "结点平衡因子异常" << endl;
// return false;
//}
平衡因子没有异常但是和结点的对不上
//if (diff != root->_bf)
//{
// //说明更新有问题
// cout << root->_kv.first << "结点平衡因子不符合实际" << endl;
// return false;
//}
pRoot的左和右如果都是AVL树,则该树一定是AVL树
把自己和自己的左右子树都检查了,递归检查
//return _IsBalance(root->_left)
// && _IsBalance(root->_right);
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
//左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppnode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
parent->_bf = subR->_bf = 0;
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
parent->_bf = subL->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 1)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
parent->_bf = 1;
subLR->_bf = 0;
subL->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = 0;
}
else
{
assert(false);
}
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
parent->_bf = -1;
subRL->_bf = 0;
subR->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
parent->_bf = 0;
subRL->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root = nullptr;
};
void Test_AVLTree1()
{
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
AVLTree t1;
for (auto e : a)
{
/* if (e == 14)
{
int x = 0;
}*/
t1.Insert(make_pair(e, e));
cout << e << "插入:" << t1.IsBalance() << endl;
}
t1.InOrder();
cout << t1.IsBalance() << endl;
}
void Test_AVLTree2()
{
srand(time(0));
const size_t N = 10;
AVLTree t;
for (size_t i = 0; i < N; ++i)
{
size_t x = rand() + i;
t.Insert(make_pair(x, x));
//cout << t.IsBalance() << endl;
}
t.InOrder();
cout << t.IsBalance() << endl;
cout << t.Height() << endl;
}