一. AVL树的概念
二叉树搜索的数据有序或接近有序时,搜索效率就会接近于n,效率低下。当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

平衡因子规定左子树高度减右子树高度。
二. AVL树模拟实现
1. AVL树节点
template <class T ,class V>
struct AvlNode
{
AvlNode<T,V>* _right;
AvlNode<T,V>* _left;
AvlNode<T,V>* _parent;//该节点的父节点
std::pair<T, V> _kv;//kv形式存储数据
int _bf;//平衡因子
AvlNode(const std::pair<T,V>& kv)
:_right(nullptr)
,_left(nullptr)
,_parent(nullptr)
,_kv(kv)//自定义参数拷贝构造,调用它自己的拷贝函数。
,_bf(0)
{
}
};
2. AVL树的整体
template <class T, class V>
class AVLStree
{
typedef AvlNode<T, V> node;
public:
private:
node* root = nullptr;
};
3. AVL树的插入
- 首先AVL树插入满足二叉搜索树:
从根开始,根为空,直接在根上插入;根不为空,小于根节点的k值,往右寻找,大于往左找,按照该规则,直到找到一个为空的节点,在该位置插入。

bool insert(const pair<T, V>& kv)
{
if (_root == nullptr)//根为空,在根上插入
{
_root = new node(kv);
return true;
}
node* cur = _root;
node* parent = nullptr;
while (cur)
{
if (cur->_kv.first == kv.first)
{
return false;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
}//寻找插入位置
if (kv.first<parent->_kv.first)
{
parent->_left = new node(kv);
}
else if (kv.first > parent->_kv.first)
{
parent->_right = new node(kv);
}
}
- 插入后调整每个节点的平衡因子
while (parent)
{
if (cur==parent->_right)//当该节点右子树高度增加了,平衡因子加一。
{
parent->_bf++;
}
else if (cur == parent->_left)//当该节点左子树高度增加了,平衡因子减一。
{
parent->_bf--;
}
if (parent->_bf == 0)//平衡因子为0,不用继续向上调节了。
break;
else if (parent->_bf == 1 || parent->_bf == -1)//平衡因子为1或-1,继续向上调整。
{
cur = parent;
parent = parent->_parent;
}
else//平衡因子为2或-2,需要调节二叉树。
{
}
};

当该节点平衡因子为0,就不需要往上修改了,说明以该节点为根的子树,高度没有改变,为1或-1时,就继续向上修改。
- 旋转AVL树
当二叉树的某节点平衡因子为2或-2时,需要旋转二叉树。

如图上这种情况,5节点的左孩子的左子树上插入1层,导致平衡因子变成了-2,可以采取右旋转。

右旋就是将平衡因子为-2节点的左子树的右子树,成为-2节点的左子树,然后使-2节点,成为它原理左子节点的,右子树。(该旋转符合搜索二叉树排序规则)
我们发现旋转后平衡因子改变的只有根和左子节点的,且都变成了0,其他子树的形状保持不变因此平衡因子未改变,且树的高度也未改变,都是h+2,因此该旋转数上面的节点的平衡因子不受影响。
void RotateR(node* root)
{
node* cur = root->_left;//先拿到-2节点的左子节点
root->_left = cur->_right;//根的左子树成为左子节点的右子树
if(cur->_right)
cur->_right->_parent = root;//左子节点的右子树的父亲改为根
cur->_right = root;//让根成为左子树的右节点
cur->_parent = root->_parent;//让左子树成为新的根节点
if (root->_parent == nullptr)//判断该节点是否为_root
{
_root = cur; //_root为原来的左子节点
root->_parent = cur;
root->_bf = 0;
cur->_bf = 0;
return;
}
else if (root->_parent->_left == root)
{
root->_parent->_left = cur;
}
else
{
root->_parent->_right = cur;
}
root->_parent = cur;//原来的根的父亲改为原来的左子节点
root->_bf = 0;//将两个改变的平衡因子修改
cur->_bf = 0;
}
- AVL树左旋
上面讲了右旋,再来看左旋,左旋顾名思义,就是当树的右边插入数据导致平衡因子,超过了1或-1,从而向左边旋转的操作。
左旋满足根平衡因子为2,右子节点平衡因子为1。

void RotateL(node* root)
{
node* cur = root->_right;//先拿到-2节点的右子节点
root->_right = cur->_left;//
if (cur->_left)
cur->_left->_parent = root;//右子节点的右子树的父亲改为根
cur->_left = root;//让根成为右子树的左节点
cur->_parent = root->_parent;//让右子树成为新的根节点
if (root->_parent == nullptr)//判断该节点是否为_root
{
_root = cur; //_root改为原来的右子节点
root->_parent = cur;
root->_bf = 0;
cur->_bf = 0;
return;
}
else if (root->_parent->_left == root)
{
root->_parent->_left = cur;
}
else
{
root->_parent->_right = cur;
}
root->_parent = cur;//原来的根的父亲改为原来的右子节点
root->_bf = 0;//将两个改变的平衡因子修改
cur->_bf = 0;
}
- AVL树的左右旋转

如图某节点平衡因子为-2时,它的左子树平衡因子为1,这说明左子树的右子树高度是高于左子树的,这时需要以图中3节点,先进行左旋,最后在一5节点进行右旋。


左右旋分为三种情况,1是给-2节点左子树的右子树的左子树高度加一。如第一个图上,导致4节点平衡因子为-1,这种,经过旋转后新的树的左子树平衡因子为0,右子树平衡因子为1,根的平衡因子为0.
2是给-2节点左子树的右子树的有子树高度加一。如第二个图上,导致4节点平衡因子为1,这种,经过旋转后新的树的左子树平衡因子为-1,右子树平衡因子为0,根的平衡因子为0.
3是图3这种情况,根的左子树的右子节点就是新插入的,此时4节点平衡因子为0。这种经过旋转后,根和左子树,右子树平衡因子都为0.
void RotateLR(node* root)
{
int bf = root->_left->_right->_bf;//将-2节点左子树的右子树节点记下来
node* SubL = root->_left;
node* SubLR = root->_left->_right;//将平衡因子会改变的节点记下来。
RotateL(SubL);
RotateR(root);
if (bf == 1)//
{
SubL->_bf = -1;
SubLR->_bf = 0;
root->_bf = 0;
}
else if (bf == -1)
{
SubL->_bf = 0;
SubLR->_bf = 0;
root->_bf = 1;
}
else if (bf == 0)
{
SubL->_bf = 0;
SubLR->_bf = 0;
root->_bf = 0;
}
else
{
assert("bf erron");
}
}
- AVL树的右左旋转
右左旋转是,根节点为2时,它的右子树平衡因子为-1时,进行的旋转。先对右子树进行右旋转,再对根进行左旋转。
右左旋转同样分为3种情况:
- 右子树的左子树平衡因子为1,旋转后右子树平衡因子为0,左子树平衡因子为-1,根平衡因子为0.
- 右子树的左子树平衡因子为-1,旋转后右子树平衡因子为1,左子树平衡因子为0,根平衡因子为0.
- 右子树的左子树平衡因子为0,旋转后右子树平衡因子为0,左子树平衡因子为0,根平衡因子为0.


void RotateRL(node* root)
{
int bf = root->_right->_left->_bf;//将-2节点左子树的右子树节点记下来
node* SubR = root->_right;
node* SubRL = root->_right->_left;//将平衡因子会改变的节点记下来。
RotateR(SubR);
RotateL(root);
if (bf == 1)//
{
SubR->_bf = 0;//旋转后原来的根右子节点成为了新树根的右子节点。
SubRL->_bf = 0;//旋转后原来的根右子节点的左子节点成为了新的根。
root->_bf = -1;//旋转后原来的根成为了新树根的左子节点。
}
else if (bf == -1)
{
SubR->_bf = 1;
SubRL->_bf = 0;
root->_bf = 0;
}
else if (bf == 0)
{
SubR->_bf = 0;
SubRL->_bf = 0;
root->_bf = 0;
}
else
{
assert("bf erron");
}
}
总结:无论是左右双旋还是右左双旋,影响最后根,左子节点和右子节点平衡因子的因素是,高度改变的子树的平衡因子。若为-1,则旋转后的新树,左子节点平衡因子为0,右为1.若为1,则旋转后的新树,左子节点平衡因子为-1,右为0。若为0,则旋转后的新树,左右子节点平衡因子都为0.
- 二叉树高度和节点数量查询
{
publi:
void Inoder()
{
_Inoder(_root);
}
int hight()
{
return _hight(_root);
}
int size()
{
return _size(_root);
}
private:
void _Inoder(node* root)
{
if (root == nullptr)
return;
_Inoder(root->_left);
cout << "K: " << root->_kv.first << ";" << "V: " << root->_kv.second << ";" << "bf: " << root->_bf << endl;
_Inoder(root->_right);
}
int _hight(node* root)
{
if (root == nullptr)
return 0;
return max(_hight(root->_left), _hight(root->_right)) + 1;
}
int _size(node* root)
{
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
- 判断二叉树是否为AVL平衡
bool _isblance(node* root)
{
if (root == nullptr)
return true;
int lefthight = _hight(root->_left);
int righthight = _hight(root->_right);
if (lefthight - righthight >= 2 || lefthight - righthight <= -2)
{
return false;
}
return _isblance(root->_left)&&_isblance(root->_right);
}
三. 整体代码
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
template <class T ,class V>
struct AvlNode
{
AvlNode<T,V>* _right;
AvlNode<T,V>* _left;
AvlNode<T,V>* _parent;
std::pair<T, V> _kv;
int _bf;
AvlNode(const std::pair<T,V>& kv)
:_right(nullptr)
,_left(nullptr)
,_parent(nullptr)
,_kv(kv)
,_bf(0)
{
}
};
template <class T, class V>
class AVLStree
{
typedef AvlNode<T, V> node;
public:
bool insert(const pair<T, V>& kv)
{
if (_root == nullptr)
{
_root = new node(kv);
return true;
}
node* cur = _root;
node* parent = nullptr;
while (cur)
{
if (cur->_kv.first == kv.first)
{
return false;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
}
if (kv.first<parent->_kv.first)
{
parent->_left = new node(kv);
cur = parent->_left;
cur->_parent = parent;
}
else if (kv.first > parent->_kv.first)
{
parent->_right = new node(kv);
cur = parent->_right;
cur->_parent = parent;
}
while (parent)
{
if (cur==parent->_right)
{
parent->_bf++;
}
else if (cur == parent->_left)
{
parent->_bf--;
}
if (parent->_bf == 0)
break;
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
else
{
if (parent->_bf == -2 && parent->_left->_bf==-1)//-2的左子节点,的左子树高度加1,右旋
{
RotateR(parent);
return true;
}
else if (parent->_bf == 2 && parent->_right->_bf == 1)//-2的左子节点,的左子树高度加1,右旋
{
RotateL(parent);
return true;
}
else if (parent->_bf == -2 && parent->_left->_bf == 1)
{
RotateLR(parent);
return true;
}
else if (parent->_bf == 2 && parent->_right->_bf == -1)
{
RotateRL(parent);
return true;
}
}
};
}
void RotateR(node* root)
{
node* cur = root->_left;//先拿到-2节点的左子节点
root->_left = cur->_right;//根的左子树成为左子节点的右子树
if(cur->_right)
cur->_right->_parent = root;//左子节点的右子树的父亲改为根
cur->_right = root;//让根成为左子树的右节点
cur->_parent = root->_parent;//让左子树成为新的根节点
if (root->_parent == nullptr)//判断该节点是否为_root
{
_root = cur; //_root为原来的左子节点
root->_parent = cur;
root->_bf = 0;
cur->_bf = 0;
return;
}
else if (root->_parent->_left == root)
{
root->_parent->_left = cur;
}
else
{
root->_parent->_right = cur;
}
root->_parent = cur;//原来的根的父亲改为原来的左子节点
root->_bf = 0;//将两个改变的平衡因子修改
cur->_bf = 0;
}
void RotateL(node* root)
{
node* cur = root->_right;//先拿到-2节点的右子节点
root->_right = cur->_left;//
if (cur->_left)
cur->_left->_parent = root;//右子节点的右子树的父亲改为根
cur->_left = root;//让根成为右子树的左节点
cur->_parent = root->_parent;//让右子树成为新的根节点
if (root->_parent == nullptr)//判断该节点是否为_root
{
_root = cur; //_root为原来的左子节点
root->_parent = cur;
root->_bf = 0;
cur->_bf = 0;
return;
}
else if (root->_parent->_left == root)
{
root->_parent->_left = cur;
}
else
{
root->_parent->_right = cur;
}
root->_parent = cur;//原来的根的父亲改为原来的右子节点
root->_bf = 0;//将两个改变的平衡因子修改
cur->_bf = 0;
}
void RotateLR(node* root)
{
int bf = root->_left->_right->_bf;//将-2节点左子树的右子树节点记下来
node* SubL = root->_left;
node* SubLR = root->_left->_right;//将平衡因子会改变的节点记下来。
RotateL(SubL);
RotateR(root);
if (bf == 1)//
{
SubL->_bf = -1;
SubLR->_bf = 0;
root->_bf = 0;
}
else if (bf == -1)
{
SubL->_bf = 0;
SubLR->_bf = 0;
root->_bf = 1;
}
else if (bf == 0)
{
SubL->_bf = 0;
SubLR->_bf = 0;
root->_bf = 0;
}
else
{
assert("bf erron");
}
}
void RotateRL(node* root)
{
int bf = root->_right->_left->_bf;//将-2节点左子树的右子树节点记下来
node* SubR = root->_right;
node* SubRL = root->_right->_left;//将平衡因子会改变的节点记下来。
RotateR(SubR);
RotateL(root);
if (bf == 1)//
{
SubR->_bf = 0;//旋转后原来的根右子节点成为了新树根的右子节点。
SubRL->_bf = 0;//旋转后原来的根右子节点的左子节点成为了新的根。
root->_bf = -1;//旋转后原来的根成为了新树根的左子节点。
}
else if (bf == -1)
{
SubR->_bf = 1;
SubRL->_bf = 0;
root->_bf = 0;
}
else if (bf == 0)
{
SubR->_bf = 0;
SubRL->_bf = 0;
root->_bf = 0;
}
else
{
assert("bf erron");
}
}
void Inoder()
{
_Inoder(_root);
}
int hight()
{
return _hight(_root);
}
int size()
{
return _size(_root);
}
bool isblance()
{
return _isblance(_root);
}
private:
void _Inoder(node* root)
{
if (root == nullptr)
return;
_Inoder(root->_left);
cout << "K: " << root->_kv.first << ";" << "V: " << root->_kv.second << ";" << "bf: " << root->_bf << endl;
_Inoder(root->_right);
}
int _hight(node* root)
{
if (root == nullptr)
return 0;
return max(_hight(root->_left), _hight(root->_right)) + 1;
}
int _size(node* root)
{
if (root == nullptr)
return 0;
return _size(root->_left) + _size(root->_right) + 1;
}
bool _isblance(node* root)
{
if (root == nullptr)
return true;
int lefthight = _hight(root->_left);
int righthight = _hight(root->_right);
if (lefthight - righthight >= 2 || lefthight - righthight <= -2)
{
return false;
}
return _isblance(root->_left)&&_isblance(root->_right);
}
private:
node* _root = nullptr;
};
858

被折叠的 条评论
为什么被折叠?



