目录
一、AVL树的概念
二叉搜索树可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于再顺序表中搜索元素,效率低下。
因此,两位俄罗斯数学家便发明了一种解决上述问题的方法:
当向二叉搜索树中插入新节点后,如果能保证每个结点左右子树高度之差的绝对值不超过1(需要对树的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 她的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1 / 0 /1 )
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有 n 个结点,其高度可保持在 logN,搜索的时间复杂度为 logN。
二、AVL 树节点的定义
二叉搜索树结点的定义与 AVL 树结点的定义在于:
- 三叉链模式,增加了父指针。
- 增加了平衡因子,方便我们调整平衡(可有可无,只是为了方便我们实现)
其数据的存储方式我们采用 K-value 模型。
三、AVL 树的插入
3.1 搜索树的插入
AVL 本质就是二叉搜索树,只不过 AVL 树是在二叉搜索树的基础上引入了平衡因子。即,在 AVL 树中的插入过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
我们先来实现第一步,按二叉搜索树的方式插入新节点。
这里就不过多介绍了,上一篇讲解过了二叉搜索树的插入。二叉搜索树的插入
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->_left = cur;
}
//因为增加了父域,所以还要处理cur的父域
cur->_parent = parent;
}
3.2 更新平衡因子的规则
以上我们完成了节点的插入,但是插入了节点后,树可能会出现以下情况:
所以,为了防止以上情况出现,我们在插入后要调整平衡。
为了维持平衡,我们引入了平衡因子来控制树的平衡,所以我们每次插入后要更新平衡因子,如果出现失去平衡的情况,我们则要进行旋转来维持平衡。
更新平衡因子的规则:
- 新增在右边,则会让父平衡因子++,新增在左边,父平衡因子 -- ;
- 更新后,如果 parent->bf == 0,说明 parent 插入前的平衡因子是 1 or -1,插入填上了矮的一边,parent 的子树高度不变,不需要继续往上更新。
- 更新后,如果 parent->bf 为 1 或 -1, 说明parent插入前的平衡因子是0,说明左右子树高度相等,插入后有一边高,parnet 高度变了,需要继续往上更新。
- 更新后,如果 parent->bf == 2 或 -2 ,说明 parent 插入前的平衡因子是 1 or -1,已经到达平衡临界值,parent 子树进行旋转处理将树保持平衡。
- 更新后,如果 parent->bf >2 或 <-2 ,则说明插入前树已经失去的平衡,要进行代码的检查。
现在,我们要对祖先路径更新平衡因子,直到更新到根节点结束。
注释我写的非常清楚,结合上图和代码一起理解。
当规则4出现时,我们则要进行旋转来调整平衡,旋转共分为 4 种情况,左左右单旋、右右左单旋、左右左右双旋、右左右左双旋我们一一进行解决。
3.4 左单旋调整

现在我们要发现左单旋的执行规律:
1.树的右子树失去平衡(parent->bf == 2),并且新插入的节点位于右子树的右侧(cur->bf == 1)。
2. 将 cur->left 交由 parent->right 抚养,再调整父子关系,使 cur->left 指向 parent ,即完成旋转。
3.平衡因子归零。观察上图,旋转后,60 的左右子树高度相同(a、b高度为h,c的高度为 h+1,注意:60左子树的高度也为h+1,因为存在 30 节点)
接下来我们编写代码
3.3 右单旋调整

现在我们要发现右单旋的执行规律:
1.树的左子树失去平衡(parent->bf == -2),并且新插入的节点位于左子树的左侧(cur->bf == -1)。
2. 将 cur->right 交由 parent->left 抚养,再调整父子关系,使 cur->right 指向 parent ,即完成旋转。
//情况2:左左右单旋
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 (ppNode)
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
else
{
_root = subL;
subL->_parent = nullptr;
}
subL->_bf = parent->_bf = 0;
}
3.5 先左单旋再右单旋
上面我们解决了4种情况中的两种情况,对于平衡的调整还有左右左右双旋和右左右左双旋这两种情况。
其调整的条件是根的左子树的右侧插入了新的节点。
这种插入情况我们还要再细分为三种插入情况,其平衡因子会出现不同的情况:
- 在30的右子树 60 左侧进行插入。
- 在30的右子树 60 右侧进行插入。
- 30的右子树为空,插入的节点为60本身。
1. 右左插入
先分析第一种情况,如图:
此时 60 节点的左侧插入节点导致 90 节点下的左右子树高度失衡,所以我们接下来要对 90 节点下进行旋转处理。
旋转的步骤分为两步:
先对30进行左单旋,然后再对90进行右单旋
1.先对 30 进行左单旋,这样,30节点的左右子树便恢复了平衡。
2.因为 60 左子树高度高于右子树,以 90节点视角来看,符合右单旋的条件(左高右低),所以90节点进行右单旋
然后对 90 进行右单旋
2. 右右插入
以上情况是在 60 的左边进行插入,同样也有在 60 的右边进行插入,两种插入会导致 90、30的 平衡因子出现不同的情况。注意观察下图平衡因子的变化,与上图是不同的。
然后对90进行右单旋
3. 右侧新增
此时我们解决的都是 30 有左右子树,60有左右子树,还有一种可能,h为0,即60本身为新增。
然后进行旋转
好的,三种情况我们都分析完了,发现其实无论哪种情况,其步骤都经历的左单旋和右单旋,只是平衡因子因情况的不同而不同。
而这三种情况的不同都是由 60 影响的,所以我们根据 60 平衡因子的不同分别进行调整即可。
3.5 先右单旋再左单旋
同样的,右左右左双旋和左右左右双旋几乎一致,就不做过多讲解了,结合画图,仿照左右双旋,很容易写出代码。
代码如下:
else if (parent->_bf == 2 && cur->_bf == -1)
{
//右左双旋
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
//旋转
RotateR(parent->_right);
RotateL(parent);
subRL->_bf = 0;
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
四、AVL 树测试
实现了插入我们来测试一下代码。
好的,程序能正常运行,但程序能正常运行就说明我们的 AVL 就是成功的吗?我们的 AVL 树就是平衡的吗?
所以现在我们要使用检查树的高度的方式来检查树是否是平衡的。即,如果左右子树的高度差不超过1,则说明该树是平衡的,接下来我们来编写成员函数。
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 leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diffHeight = abs(leftHeight - rightHeight);
// |diffHeight|小于2 则当前树平衡。
// 还要去判断左右子树是否平衡
return abs(diffHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
好的,接下来往树中插入一万个随机数据,然后来判断其是否平衡。
14层的平衡二叉树大约有16万个节点,所以高度计算正确,通过 IsBalance 函数也判定当前树为平衡。
最后我们来玩一下AVL树,往树中插入一亿个数据,然后找到77777处的数据。结果如下:
一亿个数据,插入的效率是O(n*logN),想找到其中任意一个值,最多查找高度处。
五、代码与测试用例
#include <iostream>
#include <map>
#include <utility>
#include <assert.h>
#pragma once
using namespace std;
//AVL树结点定义
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode <K, V>* _left;
AVLTreeNode <K, V>* _right;
AVLTreeNode <K, V>* _parent;
pair<K, V> _kv; //key-value模型
int _bf; //banlance 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->_left = cur;
}
cur->_parent = parent;
// 控制平衡
// 1、更新平衡因子
while (parent)
{
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
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);
}
int Height()
{
return _Height(_root);
}
bool IsBalance()
{
return _IsBalance(_root);
}
pair<K, V> Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first > key)
{
cur = cur->_left;
}
else if (cur->_kv.first < key)
{
cur = cur->_right;
}
else
{
return cur->_kv;
}
}
return pair<int, int>(0, 0);
}
private:
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diffHeight = abs(leftHeight - rightHeight);
// |diffHeight|小于2 则当前树平衡。
// 还要去判断左右子树是否平衡
return abs(diffHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
void _Inorder(Node* root)
{
if (root)
{
_Inorder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_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 (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
subR->_bf = parent->_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 (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
subLR->_bf = 0;
if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == -1)
{
// 错的
/*parent->_bf = 0;
subL->_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)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
subRL->_bf = 0;
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
else if (bf == 0)
{
parent->_bf = 0;
subR->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root = nullptr;
};
void TestAVLTree1()
{
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
AVLTree<int, int> t1;
for (auto e : a)
{
t1.Insert(make_pair(e, e));
}
t1.Inorder();
cout << t1.Height() << endl;
if (t1.IsBalance())
cout << "平衡树" << endl;
else
cout << "非平衡树" << endl;
}
void TestAVLTree2()
{
//随机插入一万个数据
size_t N = 10000;
srand((unsigned int)time(NULL));
AVLTree<int, int> t1;
for (size_t i = 0; i < N; ++i)
{
int x = rand() % 100 + 1;
t1.Insert(make_pair(i, x));
bool IsBalance = t1.IsBalance();
printf("%-4d:%-2d ", i, x);
printf("Balance:%d\n", IsBalance);
if (!IsBalance)
{
cout << i << endl;
assert(false);
}
}
//树的高度
cout << "Tree Height:" << t1.Height() << endl;
//判断平衡
if (t1.IsBalance())
cout << "IS AVL" << endl;
else
cout << "NOT AVL" << endl;
}
void TestAVLTree3()
{
size_t N = 100000000;
srand((unsigned int)time(NULL));
AVLTree<int, int> t1;
for (size_t i = 0; i < N; ++i)
{
int x = rand() % 100 + 1;
t1.Insert(make_pair(i, x));
}
cout << t1.Height() << endl;
pair<int, int> result = t1.Find(777777);
cout << result.first << ":" << result.second << endl;
}
int main()
{
//TestAVLTree1();
//TestAVLTree2();
TestAVLTree3();
return 0;
}