目录
AVL树的概念
AVL树节点的定义
template <class T>
class AVLNode{
public:
AVLNode(const T& val=T())
:_parent(nullptr)
,_right(nullptr)
, _left(nullptr)
, _val(val)
,_bf(0)//平衡因子默认为0,因为刚插入时,没有左右子树
{
}
~AVLNode(){
Destroy(_root);
}
AVLNode<T>* _parent;//指向双亲
AVLNode<T>* _left;//左指针域
AVLNode<T>* _right;//右指针域
T _val;//值域
int _bf;//平衡因子
};
AVL树的插入
template <class T>
class AVLTree{
typedef AVLNode<T> Node;//重命名节点名
public:
AVLTree()
:_root(nullptr)
{
}
bool Insert(T val){
//按照二叉搜索树的方式插入新节点
if (_root==nullptr){
_root = new Node(val);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur){
parent = cur;
if (val>cur->_val){
cur = cur->_right;
}
else if(val<cur->_val){
cur = cur->_left;
}
else{
return false;
}
}
cur = new Node(val);
cur->_parent = parent;
if (parent->_val>cur->_val){
parent->_left = cur;
}
else{
parent->_right = cur;
}
//更新平衡因子
while (parent){parent不空,往上更新
if (parent->_left==cur){//一进入循环先更新平衡因子
parent->_bf--;
}
else{
parent->_bf++;
}
if (parent->_bf==0){//平衡因子为0,此时树一定平衡,跳出循环
break;
}
else if (parent->_bf==1||parent->_bf==-1){//说明有一条路径中增加了一个节点,可能会违反AVL树的性质,需要向上不断更新
cur = cur->_parent;
parent = parent->_parent;
}
else{//AVL树的性质遭到破坏,进行调整
if (parent->_bf==2){
if (cur->_bf==1){//左单旋
_RotateL(parent);
}
else{
_RotateRL(parent);//右左双旋
}
}
else{
if (cur->_bf==-1){
_RotateR(parent);
}
else{
_RotateLR(parent);//左右双旋
}
}
break;//调整完成后不会违反AVL树的性质了,直接跳出循环
}
}
}
private:
void Destroy(Node*& root){//销毁AVL树
if (root==nullptr){
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
Node* _root;//底层维护_root指针
};
以上代码中_root为AVL树底层维护的指针,指向AVL树的根节点,对于插入节点我们并不陌生,在平衡因子为1或者-1时,需要向上调整平衡因子也非常好理解。而当平衡因子为-2或者2时候,就需要特殊处理了,而这种旋转是比较困难的,接下来进行详细讲解。
AVL树的旋转
左单旋
新插入的节点在parent较高右子树的右侧,那么就需要左单旋,具体示意图如下:
如上图,旋转之前此树是不平衡的,经过一次左旋之后,此二叉树就变得平衡了。在上图中,只体现了节点左右指针域的变化,并没有体现_parent指针域的变化,需要在代码中注意_parent指针域的变化,且parent还可能存在_parent节点,旋转后可能还需要改变其左或者右指针域的值。旋转后需要将parent节点的平衡因子以及subR节点的平衡因子修改为0,对应代码如下:
void _RotateL(Node* parent){//左单旋
Node* pparent = parent->_parent;//记录上parent的双亲节点,可能存在,可能不存在
Node* subR = parent->_right;//记录上parent的右孩子节点,一定存在
Node* subRL = subR->_left;//记录上subRL的左孩子节点,可能存在,可能不存在
parent->_right = subRL;//直接改变parent->_right指向,不用管subRL存不存在
if (subRL){//如果subRL存在,则改变其_parent指针域的指向
subRL->_parent = parent;
}
parent->_parent = subR;
subR->_left = parent;
subR->_parent = pparent;
if (pparent==nullptr){//最后判断pparent是否为空,如果是,说明parent是根节点,就需要改变_root的指向了
_root = subR;
}
else{//pparent存在,则令pprent的左或者右指向subR
if (pparent->_left == parent){
pparent->_left = subR;
}
else{
pparent->_right = subR;
}
}
parent->_bf = 0;//最后修改平衡因子
subR->_bf = 0;
}
右单旋
右单旋可以看作是左单旋的反情况,新插入的节点在parent较高左子树的左侧,那么就需要右单旋,具体示意图如下:
右单旋对比左单旋,只是旋转方向变反了,代码的实现形式类似,如下:
void _RotateR(Node* parent){//右单旋
Node* pparent = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR){
subLR->_parent = parent;
}
parent->_parent = subL;
subL->_right = parent;
subL->_parent = pparent;
if (pparent==nullptr){
_root = subL;
}
else{
if (pparent->_left==parent){
pparent->_left = subL;
}
else{
pparent->_right = subL;
}
}
parent->_bf = 0;
subL->_bf = 0;
}
右左双旋
新插入的节点在parent较高右子树的左侧,那么就需要右左双旋了,而右左双旋后,修改平衡因子非常麻烦,需要分情况讨论:
1.parent的左子树为空,那么直接调用右单旋和左单旋即可,图示如下:
把3对应的节点作进行一次右单旋后,在把2对应的节点进行一次左单旋即可。
2. parent的左子树不空,新插入的节点在parent较高右子树的左侧的左侧,图示如下:
对 subR进行一次右单旋后,在对parent进行一次左单旋,旋转完成后,subR的平衡因子不对,需要单独修改为1。可以发现在旋转之前,subRL的平衡因子为-1,根据这一规律修改即可。
3.parent的左子树不空,新插入的节点在parent较高右子树的左侧的右侧,图示如下:
对 subR进行一次右单旋后,在对parent进行一次左单旋,旋转完成后,parent的平衡因子不对,需要单独修改为-1。可以发现在旋转之前,subRL的平衡因子为1,根据这一规律修改即可。
代码如下:
void _RotateRL(Node* parent){//右左双旋
Node* subR = parent->_right;//记录parent的_right
Node* subRL = subR->_left;//记录subR的_left
int bf = subRL->_bf;//记录subR的平衡因子,根据其值是-1还是1,进而在旋转完成后修改平衡因子
_RotateR(subR);//对subR及以下部分进行右单旋
_RotateL(parent);//对parent及以下部分进行左单旋
if (bf==1){//根据bf的值,修改parent或者subR的平衡因子
parent->_bf = -1;
}
else if (bf==-1){
subR->_bf = 1;
}
}
左右双旋
左右双旋和右边双旋的实现方式类似,新插入的节点在parent较高左子树的右侧,需要进行右左双旋,分情况讨论如下:
1.parent的右子树为空,那么直接调用左单旋和右单旋即可,图示如下:
把1对应的节点作进行一次左单旋后,在把3对应的节点进行一次右单旋即可。
2.parent的右子树不空,新插入的节点在parent较高左子树的右侧的右侧,图示如下:
对subL进行一次左单旋后,在对parent进行一次右单旋,旋转完成后,subL的平衡因子不对,需要单独修改为-1。可以发现在旋转之前,subLR的平衡因子为1,根据这一规律修改即可。
3.parent的右子树不空,新插入的节点在parent较高左子树的右侧的左侧,图示如下:
对subL进行一次左单旋后,在对parent进行一次右单旋,旋转完成后,parent的平衡因子不对,需要单独修改为1。可以发现在旋转之前,subLR的平衡因子为-1,根据这一规律修改即可。
代码如下:
void _RotateLR(Node* parent){//左右双旋
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
_RotateL(parent->_left);
_RotateR(parent);
if (bf==1){
subL->_bf = -1;
}
else if (bf==-1){
parent->_bf = 1;
}
}
完整代码
#pragma once
#include <iostream>
using namespace std;
template <class T>
class AVLNode{
public:
AVLNode(const T& val=T())
:_parent(nullptr)
,_right(nullptr)
, _left(nullptr)
, _val(val)
,_bf(0)//平衡因子默认为0,因为刚插入时,没有左右子树
{
}
~AVLNode(){
Destroy(_root);
}
AVLNode<T>* _parent;//指向双亲
AVLNode<T>* _left;//左指针域
AVLNode<T>* _right;//右指针域
T _val;//值域
int _bf;//平衡因子
};
//默认没有重复元素
template <class T>
class AVLTree{
typedef AVLNode<T> Node;//重命名节点名
public:
AVLTree()
:_root(nullptr)
{
}
bool Insert(T val){
//按照二叉搜索树的方式插入新节点
if (_root==nullptr){
_root = new Node(val);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur){
parent = cur;
if (val>cur->_val){
cur = cur->_right;
}
else if(val<cur->_val){
cur = cur->_left;
}
else{
return false;
}
}
cur = new Node(val);
cur->_parent = parent;
if (parent->_val>cur->_val){
parent->_left = cur;
}
else{
parent->_right = cur;
}
//更新平衡因子
while (parent){
if (parent->_left==cur){//一进入循环先更新平衡因子
parent->_bf--;
}
else{
parent->_bf++;
}
if (parent->_bf==0){//平衡因子为0,此时树一定平衡,跳出循环
break;
}
else if (parent->_bf==1||parent->_bf==-1){//说明有一条路径中增加了一个节点,可能会违反AVL树的性质,需要向上不断更新
cur = cur->_parent;
parent = parent->_parent;
}
else{//AVL树的性质遭到破坏,进行调整
if (parent->_bf==2){
if (cur->_bf==1){//左单旋
_RotateL(parent);
}
else{
_RotateRL(parent);//右左双旋
}
}
else{
if (cur->_bf==-1){
_RotateR(parent);
}
else{
_RotateLR(parent);//左右双旋
}
}
break;
}
}
}
void InOrder(){
cout << "中序遍历结果为:" << endl;
_InOrder(_root);
cout << endl;
}
bool InvalidAVL(){
return _InvalidAVL(_root);
}
private:
int GetLength(Node* root){//计算以_root为根的二叉树深度
if (root==nullptr){
return 0;
}
int left = GetLength(root->_left) + 1;
int right = GetLength(root->_right) + 1;
return left > right ? left : right;
}
bool _InvalidAVL(Node* root){//检查构建的二叉搜索树是否正确
if (root==nullptr){
return true;
}
int left = GetLength(root->_left);
int right = GetLength(root->_right);
if (right-left!=root->_bf){
cout << "Node:" << root->_val << endl;
cout << right - left << " " << root->_bf << endl;
return false;
}
return _InvalidAVL(root->_left)&&_InvalidAVL(root->_right);
}
void _InOrder(Node* root){//中序遍历
if (root==nullptr){
return;
}
_InOrder(root->_left);
cout << root->_val << ' ';
_InOrder(root->_right);
}
void _RotateL(Node* parent){//左单旋
Node* pparent = parent->_parent;//记录上parent的双亲节点,可能存在,可能不存在
Node* subR = parent->_right;//记录上parent的右孩子节点,一定存在
Node* subRL = subR->_left;//记录上subRL的左孩子节点,可能存在,可能不存在
parent->_right = subRL;//直接改变parent->_right指向,不用管subRL存不存在
if (subRL){//如果subRL存在,则改变其_parent指针域的指向
subRL->_parent = parent;
}
parent->_parent = subR;
subR->_left = parent;
subR->_parent = pparent;
if (pparent==nullptr){//最后判断pparent是否为空,如果是,说明parent是根节点,就需要改变_root的指向了
_root = subR;
}
else{//pparent存在,则令pprent的左或者右指向subR
if (pparent->_left == parent){
pparent->_left = subR;
}
else{
pparent->_right = subR;
}
}
parent->_bf = 0;//最后修改平衡因子
subR->_bf = 0;
}
void _RotateR(Node* parent){//右单旋
Node* pparent = parent->_parent;
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR){
subLR->_parent = parent;
}
parent->_parent = subL;
subL->_right = parent;
subL->_parent = pparent;
if (pparent==nullptr){
_root = subL;
}
else{
if (pparent->_left==parent){
pparent->_left = subL;
}
else{
pparent->_right = subL;
}
}
parent->_bf = 0;
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){
subL->_bf = -1;
}
else if (bf==-1){
parent->_bf = 1;
}
}
void _RotateRL(Node* parent){//右左双旋
Node* subR = parent->_right;//记录parent的_right
Node* subRL = subR->_left;//记录subR的_left
int bf = subRL->_bf;//记录subR的平衡因子,根据其值是-1还是1,进而在旋转完成后修改平衡因子
_RotateR(subR);//对subR及以下部分进行右单旋
_RotateL(parent);//对parent及以下部分进行左单旋
if (bf==1){//根据bf的值,修改parent或者subR的平衡因子
parent->_bf = -1;
}
else if (bf==-1){
subR->_bf = 1;
}
}
void Destroy(Node*& root){//销毁AVL树
if (root==nullptr){
return;
}
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
Node* _root;//底层维护_root指针
};