完整教程:【高阶数据结构】AVL树
目录
1. 什么是 AVL 树?
简单来说,AVL 树是一种自平衡的二叉搜索树(BST)。AVL 树又叫做平衡二叉树。
它是由两位苏联数学家 G. M. Adelson-Velsky 和 E. M. Landis 在 1962 年发明的,AVL 这个名字就来源于他们姓氏的首字母。
核心思想:在二叉搜索树的基础上,增加了一个平衡条件——对于树中的任意一个节点,其左子树和右子树的高度差不能超过 1。
这个“高度差”有一个专门的名字,叫做 平衡因子。
平衡因子 = 右子树高度 - 左子树高度
平衡因子的值只能是 -1, 0, 1 中的一个。如果出现 2 或 -2,就意味着树不平衡了,需要通过“旋转”来修复。
2. 为什么需要 AVL 树?
普通的二叉搜索树(BST)有一个致命缺点:它的性能严重依赖于插入的顺序。
最佳情况:插入顺序得当,树是完全平衡的,搜索、插入、删除的时间复杂度都是 O(log n)。
最坏情况:如果插入的数据是有序的(例如 1, 2, 3, 4, 5…),BST 会退化成一条链表,时间复杂度变为 O(n)。
AVL 树就是为了解决这个问题而生的。它通过强制维持树的平衡,保证了在最坏情况下,所有操作的时间复杂度仍然是 O(log n)。这个“维持平衡”的过程,就是我们下面要讲的 旋转。
3. AVL树的模拟实现
3.1 AVL树的定义
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template<typename K, typename V>
struct AVLTreeNode
{
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<typename K, typename V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{}
private:
Node* _root;
};
3.2 AVL树的插入
关键步骤:
- 找到节点的位置
- 生成一个节点,并把节点连接好
- 以此节点的位置开始更新
- 把当前的parent节点平衡因子更新一下(+1或-1)
- 若parent节点平衡因子为0,则该树已经平衡,插入已经完成
- 若parent节点平衡因子为-1或者1,则继续更迭parent,cur,向上更新
- 若parent节点平衡因子为-2或者2,需要进行旋转,一次旋转(无论哪个旋转),都可以让parent的平衡因子直接变成0,即完成了插入。
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
else
{
Node* parent = nullptr;
Node* cur = _root;
//1. 找到节点的位置
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//2. 生成一个节点,并把节点连接好
cur = new Node(kv);
cur->_parent = parent;//三叉链需要额外链接cur的父亲节点
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
//3. 以此节点的位置开始更新
while (parent)//当parent == nullptr的时候,无法继续向上更新,退出即可
{
//更新parent的平衡因子
if (cur->_kv.first > parent->_kv.first)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if (parent->_bf == 0)//此时父亲节点的平衡因子为0,退出即可
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)//迭代cur和parent,继续
{ //向上更新
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -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)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else//如果cur的平衡因子不符合为1或-1的情况,说明在插入前树已经不是AVL树了
{ //直接断言错误即可
assert(false);
}
break;
}
else//如果parent的平衡因子不符合为0,1或-1,2或-2的情况
{ //说明在插入前树已经不是AVL树了,直接断言错误即可
assert(false);
}
}
}
return true;
}
接下来,我们讲旋转,讲旋转前,要知道的事情。
当 parent 和 cur 的平衡因子确定时,他们的位置也就确定了。
我们讲的左单璇,parent->_bf == 2 && cur->_bf == 1,即(2,1)的情况。那么简洁操作图解一定是:
A (平衡因子=2) parent
\
B (平衡因子=1) cur
\
C (新插入)
不可能是:
A (平衡因子=2) parent
/
B (平衡因子=1) cur
/
C (新插入)
因为parent平衡因子为2,那么右子树一定出现了问题,那么cur一定出现在右边,因为cur是从插入点一步步更新过来的。
3.2.1 情况 1:右右情况(RR)(2,1) -> 左单旋
parent的平衡因子为 2 ,cur的平衡因子为 1 时,是需要使用左单旋的情况。
左单璇后,parent、cur 的平衡因子都变成 0
代码解析:
void RotateL(Node* parent)
{
ode* ppnode = parent->_parent;
ode* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = ppnode;
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
}
parent->_bf = cur->_bf = 0;
}
3.2.2 情况 2:左左情况(LL)(-2,-1) -> 右单旋
parent的平衡因子为 -2 ,cur的平衡因子为 -1 时,是需要使用右单旋的情况。
右单璇后,parent、cur 的平衡因子都变成 0
代码解析:
void RotateR(Node* parent)
{
Node* ppnode = parent->_parent;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
cur->_parent = ppnode;
}
else
{
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
parent->_bf = cur->_bf = 0;
}
3.2.3 情况 3:右左情况(RL)(2,-1) -> 右左双旋(先右旋再左旋)
parent的平衡因子为 2 ,cur的平衡因子为 -1 时,是需要使用右左单旋的情况。
右左单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。

总结:
| 旋转前的curleft平衡因子 | 旋转后的parent平衡因子 | 旋转后的cur平衡因子 | 旋转后的curleft平衡因子 | |
|---|---|---|---|---|
| 第一种情况 | 1 | -1 | 0 | 0 |
| 第二种情况 | -1 | 1 | 0 | 0 |
| 第三种情况 | 0 | 0 | 0 | 0 |
代码如下:
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(cur);
RotateL(parent);
if (bf == 0)//此时对应h == 0的情况,尽管我们调用的左单旋和右单旋中已经对parent和cur
{ //和curleft的平衡因子进行了无脑置0,但是这里为了解耦,即降低耦合性,所以
parent->_bf = 0;//我们需要手动置0,降低耦合性
cur->_bf = 0;
curleft->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
cur->_bf = 1;
curleft->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
cur->_bf = 0;
curleft->_bf = 0;
}
else//如果bf出现其它情况,那么插入前该树已经不是AVL树,所以直接断言false即可
{
assert(false);
}
}
3.2.4 情况 4:左右情况(LR)(-2,1) -> 左右双旋(先左旋再右旋)
parent的平衡因子为 -2 ,cur的平衡因子为 1 时,是需要使用左右单旋的情况。
左右单璇后,parent、cur 的平衡因子不像左旋和右璇一样都变成0。而是有不同情况。
其实就是和右左双旋同理。
总结:
| 旋转前的curright平衡因子 | 旋转后的parent平衡因子 | 旋转后的cur平衡因子 | 旋转后的curright平衡因子 | |
|---|---|---|---|---|
| 第一种情况 | 1 | 0 | -1 | 0 |
| 第二种情况 | -1 | 1 | 0 | 0 |
| 第三种情况 | 0 | 0 | 0 | 0 |
代码如下:
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(cur);
RotateR(parent);
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
cur->_bf = -1;
curright->_bf = 0;
}
else
{
assert(false);
}
}
3.2.5 旋转的性质
无论是左单旋、右单旋,还是左右双旋、右左双旋,旋转后,都是二叉搜索树。
左右双旋、右左双旋其实就是2个单旋的组合。因为左单旋、右单旋不会改变树为二叉搜索树。所以,双旋也不会改变
性质:二叉搜索树前序为升序。
即无论怎么旋转,都没有改变其前序的位置。
不信可以看看左旋、右旋的例子:

前序一直是:a 30 b 60 c
前序一直是:a 30 b 60 c
3.3 AVL树的求高以及平衡的判断
代码如下:
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return heightL > heightR ? heightL + 1 : heightR + 1;
}
bool Isbalance()
{
return Isbalance(_root);
}
bool Isbalance(Node* root)
{
if (root == nullptr)
{
return true;
}
Node* left = root->_left;
Node* right = root->_right;
int heightL = Height(left);
int heightR = Height(right);
int bf = heightR - heightL;
if (bf != root->_bf)
{
cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;
return false;
}
return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
}
4. 源代码
AVLTree.h:
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
template<typename K, typename V>
struct AVLTreeNode
{
pair<K, V> _kv;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
{}
};
template<typename K, typename V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
{}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
else
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_parent = parent;
if (cur->_kv.first > parent->_kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
while (parent)
{
if (cur->_kv.first > parent->_kv.first)
{
parent->_bf++;
}
else
{
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->_bf == -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)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
}
return true;
}
int Height()
{
return Height(_root);
}
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int heightL = Height(root->_left);
int heightR = Height(root->_right);
return heightL > heightR ? heightL + 1 : heightR + 1;
}
bool Isbalance()
{
return Isbalance(_root);
}
bool Isbalance(Node* root)
{
if (root == nullptr)
{
return true;
}
Node* left = root->_left;
Node* right = root->_right;
int heightL = Height(left);
int heightR = Height(right);
int bf = heightR - heightL;
if (bf != root->_bf)
{
cout << "平衡因子异常" << root->_kv.first << ':' << root->_bf << endl;
return false;
}
return abs(bf) <= 1 && Isbalance(left) && Isbalance(right);
}
private:
void RotateL(Node* parent)
{
Node* ppnode = parent->_parent;
Node* cur = parent->_right;
Node* curleft = cur->_left;
parent->_right = curleft;
if (curleft)
{
curleft->_parent = parent;
}
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = ppnode;
if (ppnode->_left == parent)
{
ppnode->_left = cur;
}
else
{
ppnode->_right = cur;
}
}
parent->_bf = cur->_bf = 0;
}
void RotateR(Node* parent)
{
Node* ppnode = parent->_parent;
Node* cur = parent->_left;
Node* curright = cur->_right;
parent->_left = curright;
if (curright)
{
curright->_parent = parent;
}
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = cur;
cur->_parent = ppnode;
}
else
{
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
parent->_bf = cur->_bf = 0;
}
void RotateRL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
int bf = curleft->_bf;
RotateR(cur);
RotateL(parent);
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curleft->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 0;
cur->_bf = 1;
curleft->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = -1;
cur->_bf = 0;
curleft->_bf = 0;
}
else
{
assert(false);
}
}
void RotateLR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
int bf = curright->_bf;
RotateL(cur);
RotateR(parent);
if (bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == -1)
{
parent->_bf = 1;
cur->_bf = 0;
curright->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
cur->_bf = -1;
curright->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root;
};

浙公网安备 33010602011771号