目录
3.新节点插入到较高左子树的右侧--左右,先进行左单旋,再进行右单旋
4.新节点插入到较高右子树的左侧---右左,先进行右单旋,再进行左单旋
一.AVL树的性质
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一颗二叉搜索树是高度平衡的,它就是AVL树。假设它的节点数为n,其高度可保持在,搜索时间复杂度为O(
)。
二.AVL树的节点定义
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //平衡因子balance factor
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
三.AVL树的插入
AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看为是二叉搜索树。那么它的插入过程可分为两步:
1.按照二叉搜索树的方式插入新节点
2.调整节点的平衡因子
而在这两点中,更新平衡因子尤为重要,其详细过程课分为以下几点
①.当新增节点在右时,parent->_bf需要+1,新增节点在左时,parent->_bf需要-1。
②.更新后,parent->_bf == 1 or -1,说明parent插入前的平衡因子为0,说明原左右子树高度相等,插入后有一边子树变高,此时改变了parent的高度,便需要往上继续更新平衡因子。
③.更新后,parent->_bf == 0,说明parent插入前的平衡因子为 1 or -1,说明左右子树一边高一边低,插入后两边变为一样高度,parent所在子树高度不变,不需要继续向上面的节点更新平衡因子。
④.更新后,parent->_bf == 2 or -2,说明parent插入前的平衡因子为 1 or -1,此时原左右子树已经存在一边高一边低的情况,继续插入的节点导致parent的平衡因子变为 2 or -2,平衡被打破,则parent所在子树需要旋转处理。
⑤.更新后,parent->_bf > 2 or parent->_bf < -2的值,存在这种情况,说明在插入前的就已经不是AVL树,需要检查之前的树。
下图将简略描述以上几点:
对于第四点旋转处理将在下方详细介绍。
四.AVL树的旋转处理
AVL树的旋转处理又分为四种。
1.新节点插入到较高右子树的右边---右右,进行左单旋
通过上图发现当对较高右子树的右边进行插入时,向上更新平衡因子,造成parent节点平衡因子变为2,此时需要进行旋转处理,来降低右子树的高度来达到新的平衡,可以看出将60节点变为新的parent节点,再将b子树链接到30节点的右边,便可以达到新的平衡。
具体左旋代码如下:
void RotateL(Node* parent)
{
//记父节点的右为subR
//subR的左为subRL
Node* subR = parent->_right;
Node* subRL = subR->_left;
//将父节点与subRL进行链接
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//记parent节点的上一级节点为ppNode(方便更新平衡因子)
Node* ppNode = parent->_parent;
//继续链接
subR->_left = parent;
parent->_parent = subR;
//原parent节点为根节点的情况
if (_root == subR)
{
_root = subR;
subR->_parent = nullptr;
}
else //原parent节点为一棵子树
{
//将subR与原parent的上层节点进行链接
//链接到左
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else //链接到右
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
subR->_bf = parent->_bf = 0;
}
2.新节点插入到较高左子树的左侧---左左,进行右单旋
通过上图发现当对较高左子树的左边进行插入时,向上更新平衡因子,造成parent节点平衡因子变为-2,此时需要进行旋转处理,来降低左子树的高度来达到新的平衡,可以看出将30节点变为新的parent节点,再将b子树链接到60节点的左边,便可以达到新的平衡。
具体左旋代码如下:
void RotateR(Node* parent)
{
//记父节点的左为subL
//subL的右为subLR
Node* subL = parent->_left;
Node* subLR = subL->_right;
//将subLR与父节点的进行链接
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
//记parent节点的上一级节点为ppNode(方便更新平衡因子)
Node* ppNode = parent->_parent;
//继续链接
subL->_right = parent;
parent->_parent = subL;
//原parent节点为根节点的情况
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else //原parent节点为一棵子树
{
//将subL与原parent的上层节点进行链接
//链接到左
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else //连接到右
{
ppNode->_right = subL;
}
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
3.新节点插入到较高左子树的右侧--左右,先进行左单旋,再进行右单旋
而左右双旋又可以分为三种情况(便于调整平衡因子),以上图为例。
1.b插入新增节点,引发双旋。(图中情况)
2.c插入新增节点,引发双选。
3.a/b/c/d为空树,60即为新增节点,引发双旋。
双旋的过程与单旋并没有什么区别,因此可以复用单旋的代码,需要注意的是对平衡因子的调整。
具体左右双旋代码如下:
void RotateLR(Node* parent)
{
//记父节点的左为subL
//subL的右为subLR
Node* subL = parent->_left;
Node* subLR = subL->_right;
//记录subLR的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
int bf = subLR->_bf;
//左单旋
RotateL(parent->_left);
//右单旋
RotateLR(parent);
//双旋过后subLR平衡因子为0
subLR->_bf = 0;
//对父节点和subL平衡因子进行调整的三种情况
//新增节点在subLR的右
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
//新增节点在subLR的左
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
//该节点即为新增节点
else if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
}
else
{
assert(false);
}
}
4.新节点插入到较高右子树的左侧---右左,先进行右单旋,再进行左单旋
同样右左双旋也可以分为三种情况(便于调整平衡因子),以上图为例。
1.b插入新增节点,引发双旋。
2.c插入新增节点,引发双选。(图中情况)
3.a/b/c/d为空树,60即为新增节点,引发双旋。
双旋的过程与单旋并没有什么区别,因此可以复用单旋的代码,需要注意的是对平衡因子的调整。
具体右左双旋的代码如下:
void RotateRL(Node* parent)
{
//记父节点的右为subR
//subR的左为subRL
Node* subR = parent->_right;
Node* subRL = subR->_left;
//记录subRL的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
int bf = subRL->_bf;
//右单旋
RotateR(parent->_right);
//左单旋
RotateL(parent);
//双旋后subRL的平衡因子为0
subRL->_bf = 0;
//对父节点和subR平衡因子进行调整的三种情况
//新增节点在subRL的右
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
//新增节点在subRL的左
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
//该节点即为新增节点
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
五.AVL树简单实现的整体代码
#pragma once
#include<iostream>
#include <map>
#include <string>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; //平衡因子balance factor
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{}
};
template<class K, class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& 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->_right = cur;
}
else // (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
cur->_parent = parent;
//控制平衡
//更新平衡因子
while (parent)
{
if (cur = parent->_right)
{
parent->_bf++
}
else //(cur = parent->left)
{
parent->_bf--;
}
if (parent->_bf == 0)
{
break;
}
else if (abs(parent->_bf) == 1) //向上调整
{
parent = parent->_parent;
cur = cur->_parent;
}
else if (abs(parent->_bf) == 2) //说明此时parent所在子树已不平衡,需要进行旋转平衡
{
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);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
return _IsBalance(_root);
}
private:
//计算高度
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
//递归累加
return max(Height(root->_left), Height(root->_right)) + 1;
}
//判断是否平衡
bool _IsBalance(Node* root)
{
//空树平衡
if (root == nullptr)
{
return true;
}
//递归找左子树高度
int leftHt = Height(root->_left);
//递归找右子树高度
int rightHt = Height(root->_right);
//查看平衡因子是否在(-1/0/1)的范围
int diff = rightHt - leftHt;
if (diff != root->_bf)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
//递归找左子树是否异常和递归找右子树是否异常
return abs(diff) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right)
}
void RotateL(Node* parent)
{
//记父节点的右为subR
//subR的左为subRL
Node* subR = parent->_right;
Node* subRL = subR->_left;
//将父节点与subRL进行链接
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//记parent节点的上一级节点为ppNode(方便更新平衡因子)
Node* ppNode = parent->_parent;
//继续链接
subR->_left = parent;
parent->_parent = subR;
//原parent节点为根节点的情况
if (_root == subR)
{
_root = subR;
subR->_parent = nullptr;
}
else //原parent节点为一棵子树
{
//将subR与原parent的上层节点进行链接
//链接到左
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else //链接到右
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
//记父节点的左为subL
//subL的右为subLR
Node* subL = parent->_left;
Node* subLR = subL->_right;
//将subLR与父节点的进行链接
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
//记parent节点的上一级节点为ppNode(方便更新平衡因子)
Node* ppNode = parent->_parent;
//继续链接
subL->_right = parent;
parent->_parent = subL;
//原parent节点为根节点的情况
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else //原parent节点为一棵子树
{
//将subL与原parent的上层节点进行链接
//链接到左
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else //连接到右
{
ppNode->_right = subL;
}
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
//记父节点的左为subL
//subL的右为subLR
Node* subL = parent->_left;
Node* subLR = subL->_right;
//记录subLR的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
int bf = subLR->_bf;
//左单旋
RotateL(parent->_left);
//右单旋
RotateLR(parent);
//双旋过后subLR平衡因子为0
subLR->_bf = 0;
//对父节点和subL平衡因子进行调整的三种情况
//新增节点在subLR的右
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
//新增节点在subLR的左
else if (bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
//该节点即为新增节点
else if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
}
else
{
assert(false);
}
}
void RotateRL(Node* parent)
{
//记父节点的右为subR
//subR的左为subRL
Node* subR = parent->_right;
Node* subRL = subR->_left;
//记录subRL的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
int bf = subRL->_bf;
//右单旋
RotateR(parent->_right);
//左单旋
RotateL(parent);
//双旋后subRL的平衡因子为0
subRL->_bf = 0;
//对父节点和subR平衡因子进行调整的三种情况
//新增节点在subRL的右
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
//新增节点在subRL的左
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
//该节点即为新增节点
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
private:
Node* _root = nullptrl;
};