
🌈 say-fall:个人主页 🚀 专栏:《手把手教你学会C++》 | 《系统深入Linux操作系统》 | 《数据结构与算法》 | 《小游戏与项目》 💪 格言:做好你自己,才能吸引更多人,与他们共赢,这才是最好的成长方式。
文章目录
- [🔥 前言](#🔥 前言)
- [📚 一、AVL树的概念](#📚 一、AVL树的概念)
- [🏗️ 二、AVL树的结构设计](#🏗️ 二、AVL树的结构设计)
- [⚙️ 三、插入操作详解](#⚙️ 三、插入操作详解)
-
- [3.1 插入的基本思路](#3.1 插入的基本思路)
- [3.2 平衡因子的更新](#3.2 平衡因子的更新)
- [3.3 旋转操作](#3.3 旋转操作)
-
- [📍 左单旋(情况:右边高,且右孩子的右子树高)](#📍 左单旋(情况:右边高,且右孩子的右子树高))
- [📍 右单旋(情况:左边高,且左孩子的左子树高)](#📍 右单旋(情况:左边高,且左孩子的左子树高))
- [📍 左右双旋(情况:左边高,但左孩子的右子树高)](#📍 左右双旋(情况:左边高,但左孩子的右子树高))
- [📍 右左双旋(情况:右边高,但右孩子的左子树高)](#📍 右左双旋(情况:右边高,但右孩子的左子树高))
- [📊 四、四种旋转的判断](#📊 四、四种旋转的判断)
- [🧪 五、完整代码实现](#🧪 五、完整代码实现)
-
- [AVLTree.h 完整代码](#AVLTree.h 完整代码)
- [测试代码 Test.cpp](#测试代码 Test.cpp)
- [📈 六、性能分析](#📈 六、性能分析)
- [🎯 七、总结](#🎯 七、总结)
- 本节完...
🔥 前言
AVL树,听起来很高大上?其实它是平衡二叉搜索树的一种经典实现!今天我们就来手撕AVL树,用C++实现一个完整版本 🚀
📚 一、AVL树的概念
什么是AVL树?
AVL树(由发明者 G.M. Adelson-Velsky 和 E.M. Landis 命名)是一种自平衡的二叉搜索树。
核心特点:左右子树的高度差(平衡因子)的绝对值不超过 1
简单来说,就是:
✅ AVL树 ❌ 非AVL树
4 4
/ \ /
2 6 2
/ \ \
1 3 3
\
5
为什么需要AVL树?
普通的二叉搜索树在极端情况下会退化成链表,时间复杂度从 O(log n) 退化成 O(n)!
| 操作 | 理想情况(平衡) | 最坏情况(退化成链表) |
|---|---|---|
| 查找 | O(log n) | O(n) |
| 插入 | O(log n) | O(n) |
| 删除 | O(log n) | O(n) |
🏗️ 二、AVL树的结构设计
节点结构
cpp
template <class K, class V>
struct AVLTreeNode
{
pair<K, V> _kv; // 键值对
AVLTreeNode <K,V>* _left; // 左孩子
AVLTreeNode <K,V>* _right; // 右孩子
AVLTreeNode <K,V>* _parent; // 父节点(用于回溯)
int _bf; // Balance Factor 平衡因子 = 右子树高度 - 左子树高度
AVLTreeNode(const pair<K, V>& kv)
: _kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{
}
};
小贴士:平衡因子
_bf的取值只能是 -1, 0, 1,否则就需要旋转调整!
⚙️ 三、插入操作详解
3.1 插入的基本思路
📌 插入步骤:
① 按BST规则找到插入位置
② 将新节点插入并链接父节点
③ 从插入位置向上回溯,更新平衡因子
④ 当平衡因子变为 ±2 时,进行旋转调整
3.2 平衡因子的更新
关键点:平衡因子的更新是理解AVL树的核心!
cpp
// 更新平衡因子
if (parent->_left == cur) // 左子树插入
{
parent->_bf--;
}
else // 右子树插入
{
parent->_bf++;
}
| 平衡因子变化 | 含义 | 处理方式 |
|---|---|---|
| 0 | 原本一边轻一边重,插入后平衡了 | ✅ 停止回溯 |
| ±1 | 原本平衡,现在一边更高了 | 🔄 继续向上更新 |
| ±2 | 失衡了!需要旋转 | 🔧 旋转调整 |
3.3 旋转操作
旋转的本质:让树重新恢复平衡,同时保持BST的有序性
📍 左单旋(情况:右边高,且右孩子的右子树高)
旋转前: 旋转后:
p p'
/ \ / \
... subR → ... subR
/ \ / \
... subRL ... subRL
↓ ↓
(新节点) (新节点)
cpp
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* Pparent = parent->_parent;
// 重新链接
parent->_right = subRL;
if (subRL) subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
// 处理根节点
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
subR->_parent = Pparent;
if (Pparent->_left == parent)
Pparent->_left = subR;
else
Pparent->_right = subR;
}
subR->_bf = 0;
parent->_bf = 0;
}
📍 右单旋(情况:左边高,且左孩子的左子树高)
旋转前: 旋转后:
p p'
/ \ / \
subL ... → subL ...
/ \ / \
subLR ... subLR ...
↓ ↓
(新节点) (新节点)
cpp
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* subLR = cur->_right;
Node* Pparent = parent->_parent;
// 重新链接
cur->_right = parent;
parent->_parent = cur;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
// 处理根节点
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = Pparent;
if (Pparent->_left == parent)
Pparent->_left = cur;
else
Pparent->_right = cur;
}
cur->_bf = 0;
parent->_bf = 0;
}
📍 左右双旋(情况:左边高,但左孩子的右子树高)
旋转前: 先左旋: 再右旋:
p p p'
/ \ / \ / \
subL subR → cur subR → subL subR
/ \ / \ / \
... subLR ... ... ... ...
↓ ↓ ↓
(新节点) (新节点) (新节点)
cpp
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf; // 保存旋转前的平衡因子,用于后续调整
RotateL(parent->_left); // 先左旋
RotateR(parent); // 再右旋
// 根据不同情况调整平衡因子
if (bf == -1) // subLR插入在左子树
{
parent->_bf = 1;
subL->_bf = 0;
}
else if (bf == 1) // subLR插入在右子树
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == 0) // subLR就是新插入的节点
{
parent->_bf = 0;
subL->_bf = 0;
}
}
📍 右左双旋(情况:右边高,但右孩子的左子树高)
这是左右双旋的镜像情况,原理相同!
cpp
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right); // 先右旋
RotateL(parent); // 再左旋
if (bf == 1)
{
subRL->_bf = 0;
parent->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 0;
}
}
📊 四、四种旋转的判断
旋转类型判断口诀:看爹看儿,爹2儿1/爹-2儿-1
| 父平衡因子 | 子平衡因子 | 旋转类型 | 图示 |
|---|---|---|---|
| -2 | -1 | 右单旋 | 左左 |
| 2 | 1 | 左单旋 | 右右 |
| -2 | 1 | 左右双旋 | 左右 |
| 2 | -1 | 右左双旋 | 右左 |
cpp
// 旋转判断代码
if (parent->_bf == -2 && cur->_bf == -1) // 左左 → 右单旋
{
RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1) // 右右 → 左单旋
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) // 左右 → 左右双旋
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1) // 右左 → 右左双旋
{
RotateRL(parent);
}
🧪 五、完整代码实现
AVLTree.h 完整代码
cpp
#pragma once
#include<iostream>
#include<utility>
#include<cassert>
using namespace std;
template <class K, class V>
struct AVLTreeNode
{
pair<K, V> _kv;
AVLTreeNode <K,V>* _left;
AVLTreeNode <K,V>* _right;
AVLTreeNode <K,V>* _parent;
int _bf; // Balance Factor
AVLTreeNode(const pair<K, V>& kv)
: _kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _bf(0)
{
}
};
template <class K, class V>
class AVLTree
{
using Node = AVLTreeNode<K, V>;
public:
// 插入
bool Insert(const pair<K,V>& kv)
{
// 1. 空树直接插入
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
// 2. 找到插入位置
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; // -key已存在
}
}
// 3. 插入新节点
cur = new Node(kv);
if (parent->_kv.first < kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
// 4. 向上更新平衡因子
while (parent)
{
if (parent->_left == cur)
parent->_bf--;
else
parent->_bf++;
// 判断是否需要继续更新或旋转
if (parent->_bf == 0)
break; // 平衡了,停止
else if (parent->_bf == 1 || parent->_bf == -1)
{
// 继续向上更新
cur = parent;
parent = cur->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 需要旋转
if (parent->_bf == -2 && cur->_bf == -1)
RotateR(parent);
else if (parent->_bf == 2 && cur->_bf == 1)
RotateL(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;
}
// 获取高度
int Height()
{
return _Height(_root);
}
// 获取节点数
int Size()
{
return _Size(_root);
}
// 检查是否是AVL树
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
// 查找
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
cur = cur->_right;
else if (cur->_kv.first > key)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
private:
// 右单旋
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* subLR = cur->_right;
Node* Pparent = parent->_parent;
cur->_right = parent;
parent->_parent = cur;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
if (parent == _root)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
cur->_parent = Pparent;
if (Pparent->_left == parent)
Pparent->_left = cur;
else
Pparent->_right = cur;
}
cur->_bf = 0;
parent->_bf = 0;
}
// 左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* Pparent = parent->_parent;
parent->_right = subRL;
if (subRL) subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
subR->_parent = Pparent;
if (Pparent->_left == parent)
Pparent->_left = subR;
else
Pparent->_right = subR;
}
subR->_bf = 0;
parent->_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)
{
parent->_bf = 1;
subL->_bf = 0;
}
else if (bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}
else if (bf == 0)
{
parent->_bf = 0;
subL->_bf = 0;
}
}
// 右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subRL->_bf = 0;
parent->_bf = -1;
subR->_bf = 0;
}
else if (bf == -1)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 1;
}
else if (bf == 0)
{
subRL->_bf = 0;
parent->_bf = 0;
subR->_bf = 0;
}
}
void _InOrder(Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr) return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _Size(Node* root)
{
if (root == nullptr) return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
bool _IsBalanceTree(Node* root)
{
if (root == nullptr) return true;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;
if (abs(diff) >= 2)
{
cout << root->_kv.first << "高度差异常" << endl;
return false;
}
if (root->_bf != diff)
{
cout << root->_kv.first << "平衡因子异常" << endl;
return false;
}
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
Node* _root = nullptr;
};
测试代码 Test.cpp
cpp
#include "AVLTree.h"
void TestAVLTree1()
{
AVLTree<int, int> t;
// 常规测试用例
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
// 双旋场景测试用例
//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
t.Insert({ e, e });
}
t.InOrder();
cout << "是否平衡: " << t.IsBalanceTree() << endl;
}
// 性能测试:插入100万个随机数
void TestAVLTree2()
{
const int N = 1000000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand() + i);
}
size_t begin2 = clock();
AVLTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
}
size_t end2 = clock();
cout << "插入耗时: " << end2 - begin2 << " ms" << endl;
cout << "是否平衡: " << t.IsBalanceTree() << endl;
cout << "树高度: " << t.Height() << endl;
cout << "节点数: " << t.Size() << endl;
size_t begin1 = clock();
for (auto e : v)
{
t.Find(e);
}
size_t end1 = clock();
cout << "查找耗时: " << end1 - begin1 << " ms" << endl;
}
int main()
{
TestAVLTree2();
return 0;
}
📈 六、性能分析
AVL树各项操作的时间复杂度都是 O(log n)!
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 查找 | O(log n) | 始终保持平衡 |
| 插入 | O(log n) | 最多旋转两次 |
| 删除 | O(log n) | 最多旋转O(log n)次 |
| 获取高度 | O(n) | 需要遍历子树 |
| 中序遍历 | O(n) | 遍历所有节点 |
🎯 七、总结
🎉 恭喜你掌握了AVL树!
📌 今日收获:
✅ 理解了AVL树的自平衡原理
✅ 掌握了四种旋转操作(左单旋、右单旋、左右双旋、右左双旋)
✅ 学会了如何更新平衡因子
✅ 能够实现完整的AVL树
💡 思考题:AVL树和红黑树有什么区别?什么时候该用AVL树,什么时候该用红黑树?
本节完...
如果觉得有帮助,记得 点赞 + 关注 + 收藏 哦!