当前位置: 首页 > news >正文

C++ AVL树实现详解:理论+代码+图解

C++ AVL树实现详解:理论+代码+图解


文章目录

  • C++ AVL树实现详解:理论+代码+图解
  • 一、AVL的概念
  • 二、AVL树的实现
    • 2.1 AVL的结构
    • 2.2 AVL树的插入
      • 2.2.1 AVL树插入一个值的大概过程
      • 2.2.2 平衡因子更新
      • 2.2.3 插入结点及更新平衡因子的代码实现
    • 2.3 旋转
      • 2.3.1 旋转的原则
      • 2.3.2 左旋
      • 2.3.3 右旋
    • 2.4 旋转的四种情况
      • 2.4.1 LL型
      • 2.4.2 RR型
      • 2.4.3 LR型
      • 2.4.4 RL型
    • 2.5 AVL树插入的注意事项
    • 2.6 旋转的代码实现
      • 2.6.1 左单旋代码实现(LL)
      • 2.6.2 右单旋代码实现(RR)
      • 2.6.3 左右双旋代码实现(LR)
      • 2.6.4 右左双旋代码实现(RL)
    • 2.7 AVL树的查找
    • 2.8 AVL树平衡检测
      • 2.8.1 AVL树的高度
      • 2.8.2 AVL树的平衡
    • 2.9 AVL树删除
  • 三、整体源代码


一、AVL的概念

在这里插入图片描述


二、AVL树的实现

AVL树查找,插入,构建,删除的过程和二叉搜索树一致,只是失衡的时候需要调整(旋转)

2.1 AVL的结构

这里和二叉搜索树基本一致,多了一个平衡因子
在这里插入图片描述


2.2 AVL树的插入

2.2.1 AVL树插入一个值的大概过程

在这里插入图片描述


2.2.2 平衡因子更新

在这里插入图片描述


2.2.3 插入结点及更新平衡因子的代码实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


代码如下(示例):

	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;// 更新平衡因⼦while (parent){// 更新平衡因⼦if (cur == parent->_left)parent->_bf--;elseparent->_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){// 不平衡了,旋转处理break;}else{assert(false);}}return true;}

2.3 旋转

2.3.1 旋转的原则

在这里插入图片描述 这里的平衡因子我们统一用左子树高度-右子树高度进行计算


在这里插入图片描述


2.3.2 左旋

在这里插入图片描述


在这里插入图片描述在这里插入图片描述


2.3.3 右旋

在这里插入图片描述


在这里插入图片描述在这里插入图片描述


2.4 旋转的四种情况

在这里插入图片描述在这里插入图片描述


2.4.1 LL型

在这里插入图片描述
在这里插入图片描述


2.4.2 RR型

在这里插入图片描述

在这里插入图片描述


2.4.3 LR型

在这里插入图片描述
在这里插入图片描述


2.4.4 RL型

在这里插入图片描述
在这里插入图片描述


2.5 AVL树插入的注意事项

注意:这里只是插入这样做,删除不是这样做的
在这里插入图片描述在这里插入图片描述


2.6 旋转的代码实现

2.6.1 左单旋代码实现(LL)

在这里插入图片描述

代码如下(示例):

void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if(subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
parent->_bf = subR->_bf = 0;
}

2.6.2 右单旋代码实现(RR)

在这里插入图片描述

代码如下(示例):

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;// 需要注意除了要修改孩⼦指针指向,还是修改⽗亲parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;// parent有可能是整棵树的根,也可能是局部的⼦树// 如果是整棵树的根,要修改_root// 如果是局部的指针要跟上⼀层链接if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = subL->_bf = 0;}

左右双旋和右左双旋,主要是在函数里面调用 左单旋和右单旋


2.6.3 左右双旋代码实现(LR)

在这里插入图片描述

代码如下(示例):

void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
RotateR(parent);
if (bf == 0)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == -1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if(bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}

2.6.4 右左双旋代码实现(RL)

在这里插入图片描述

代码如下(示例):

void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == -1)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}

2.7 AVL树的查找

按照二叉搜索树逻辑实现即可,搜索效率为O(logN)
在这里插入图片描述

代码如下(示例):

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;}

2.8 AVL树平衡检测

我们实现的AVL树是否合格,我们通过检查左右子树高度差的程序进行反向验证,同时检查一下节点的平衡因子更新是否出现了问题!


2.8.1 AVL树的高度

这里采用递归的方式,与二叉树求根的高度方法一致
在这里插入图片描述

代码如下(示例):

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;}

2.8.2 AVL树的平衡

这里同样也采用了递归的方法进行检测
在这里插入图片描述

代码如下(示例):

bool _IsBalanceTree(Node* root){// 空树也是AVL树if (nullptr == root)return true;// 计算pRoot结点的平衡因⼦:即pRoot左右⼦树的⾼度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因⼦与pRoot的平衡因⼦不相等,或者// pRoot平衡因⼦的绝对值超过1,则⼀定不是AVL树if (abs(diff) >= 2){cout << root->_kv.first << "⾼度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因⼦异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树⼀定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}

2.9 AVL树删除

在这里插入图片描述
在这里插入图片描述


代码如下(示例):

void erase(const K& val){//递归删除_root = _erase(_root, val);}Node* _erase(Node* node, const K& val){// 如果当前节点为空,直接返回空指针if (node == nullptr){return nullptr;}// 如果当前节点的键值大于要删除的键值,在左子树中删除if (node->_kv.first > val){node->_left = _erase(node->_left, val);//更新节点的父节点if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况一删除节点都在左边if (bf == 2){//右侧的平衡因子int right_bf = node->_right->_bf;if (right_bf >= 0){Node* subR = node->_right;Node* parent = node;RotateL(node);//高度相等,要重新更新平衡因子if (right_bf == 0){subR->_bf = -1;parent->_bf = 1;}}else{RotateRL(node);}}}// 如果当前节点的键值小于要删除的键值,在右子树中删除else if (node->_kv.first < val){node->_right = _erase(node->_right, val);//更新节点的父节点if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况二删除节点都在右边if (bf == -2){//左侧的平衡因子int left_bf = node->_left->_bf;if (left_bf <= 0){Node* subL = node->_left;Node* parent = node;RotateR(node);//高度相等,要重新更新平衡因子if (left_bf == 0){subL->_bf = 1;parent->_bf = -1;}}else{RotateLR(node);}}}// 找到要删除的节点else{// 如果节点有两个孩子if (node->_left != nullptr && node->_right != nullptr){// 如果左子树高度大于等于右子树高度if (Hegiht(node->_left) >= Hegiht(node->_right)){// 找到左子树中的最大节点Node* prev = node->_left;while (prev->_right)prev = prev->_right;int target = prev->_kv.first;// 将当前节点的值替换为左子树中的最大节点的值node->_kv = prev->_kv;// 在左子树中删除该最大节点node->_left = _erase(node->_left, target);if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}else{// 找到右子树中的最小节点Node* post = node->_right;while (post->_left)post = post->_left;// 将当前节点的值替换为右子树中的最小节点的值int target = post->_kv.first;node->_kv = post->_kv;// 在右子树中删除该最小节点node->_right = _erase(node->_right, target);if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}}// 如果节点最多有一个孩子else{// 如果有左孩子if (node->_left != nullptr){// 保存左孩子指针Node* left = node->_left;// 删除当前节点delete node;// 返回左孩子指针return left;}// 如果有右孩子else if (node->_right != nullptr){// 保存右孩子指针Node* right = node->_right;// 删除当前节点delete node;// 返回右孩子指针return right;}// 如果没有孩子else{// 返回空指针delete node;return nullptr;}}}// 返回调整后的节点指针return node;}

三、整体源代码

代码如下(示例):

#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{// 需要parent指针,后续更新平衡因⼦可以看到pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};
template<class K, class V>
class 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;// 更新平衡因⼦while (parent){// 更新平衡因⼦if (cur == parent->_left)parent->_bf--;elseparent->_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){// 不平衡了,旋转处理break;}else{assert(false);}}return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;// 需要注意除了要修改孩⼦指针指向,还是修改⽗亲parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;// parent有可能是整棵树的根,也可能是局部的⼦树// 如果是整棵树的根,要修改_root// 如果是局部的指针要跟上⼀层链接if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = 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 == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_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);if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}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;}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;}bool _IsBalanceTree(Node* root){// 空树也是AVL树if (nullptr == root)return true;// 计算pRoot结点的平衡因⼦:即pRoot左右⼦树的⾼度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因⼦与pRoot的平衡因⼦不相等,或者// pRoot平衡因⼦的绝对值超过1,则⼀定不是AVL树if (abs(diff) >= 2){cout << root->_kv.first << "⾼度差异常" << endl;return false;}if (root->_bf != diff){cout << root->_kv.first << "平衡因⼦异常" << endl;return false;}// pRoot的左和右如果都是AVL树,则该树⼀定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}// 测试代码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;}// 插⼊⼀堆随机值,测试平衡,顺便测试⼀下⾼度和性能等void TestAVLTree2(){const int N = 100000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}ize_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << t.IsBalanceTree() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值/*for (auto e : v){t.Find(e);}*/// 随机值for (size_t i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;}void erase(const K& val){//递归删除_root = _erase(_root, val);}Node* _erase(Node* node, const K& val){// 如果当前节点为空,直接返回空指针if (node == nullptr){return nullptr;}// 如果当前节点的键值大于要删除的键值,在左子树中删除if (node->_kv.first > val){node->_left = _erase(node->_left, val);//更新节点的父节点if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况一删除节点都在左边if (bf == 2){//右侧的平衡因子int right_bf = node->_right->_bf;if (right_bf >= 0){Node* subR = node->_right;Node* parent = node;RotateL(node);//高度相等,要重新更新平衡因子if (right_bf == 0){subR->_bf = -1;parent->_bf = 1;}}else{RotateRL(node);}}}// 如果当前节点的键值小于要删除的键值,在右子树中删除else if (node->_kv.first < val){node->_right = _erase(node->_right, val);//更新节点的父节点if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);int bf = node->_bf;//情况二删除节点都在右边if (bf == -2){//左侧的平衡因子int left_bf = node->_left->_bf;if (left_bf <= 0){Node* subL = node->_left;Node* parent = node;RotateR(node);//高度相等,要重新更新平衡因子if (left_bf == 0){subL->_bf = 1;parent->_bf = -1;}}else{RotateLR(node);}}}// 找到要删除的节点else{// 如果节点有两个孩子if (node->_left != nullptr && node->_right != nullptr){// 如果左子树高度大于等于右子树高度if (Hegiht(node->_left) >= Hegiht(node->_right)){// 找到左子树中的最大节点Node* prev = node->_left;while (prev->_right)prev = prev->_right;int target = prev->_kv.first;// 将当前节点的值替换为左子树中的最大节点的值node->_kv = prev->_kv;// 在左子树中删除该最大节点node->_left = _erase(node->_left, target);if (node->_left)node->_left->_parent = node;// 调整节点的平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}else{// 找到右子树中的最小节点Node* post = node->_right;while (post->_left)post = post->_left;// 将当前节点的值替换为右子树中的最小节点的值int target = post->_kv.first;node->_kv = post->_kv;// 在右子树中删除该最小节点node->_right = _erase(node->_right, target);if (node->_right)node->_right->_parent = node;// 调整节点平衡因子node->_bf = Hegiht(node->_right) - Hegiht(node->_left);}}// 如果节点最多有一个孩子else{// 如果有左孩子if (node->_left != nullptr){// 保存左孩子指针Node* left = node->_left;// 删除当前节点delete node;// 返回左孩子指针return left;}// 如果有右孩子else if (node->_right != nullptr){// 保存右孩子指针Node* right = node->_right;// 删除当前节点delete node;// 返回右孩子指针return right;}// 如果没有孩子else{// 返回空指针delete node;return nullptr;}}}// 返回调整后的节点指针return node;}private:Node* _root = nullptr;
};
http://www.dtcms.com/a/320299.html

相关文章:

  • 使用Cloud Document Converter将飞书文档导出为markdown
  • 神经网络中一般都包含哪些关键层,每一层的作用是什么?
  • Gemini-CLI-项目原理流程总结
  • 大模型2位量化原理解析
  • Redis面试精讲 Day 16:Redis性能监控与分析工具
  • Microsoft Office PowerPoint 制作简单的游戏素材
  • 腾讯位置服务 —— 预估订单路线金额(使用Drools规则引擎处理)
  • Gitee上免费搭建博客
  • 基于C++深度学习 (NCNN、MNN、OpenVINO)OpenCV 等实践
  • 第二集 测试概念
  • 8月7号打卡
  • python---函数的形参与实参
  • C++的入门学习
  • 拷贝数组练习
  • 瞬态吸收光谱仪的基本原理
  • Ubuntu 系统 Docker 启动失败(iptables/nf\_tables)
  • 【CodeButty + 自制MCP】给AI装上翅膀,快速绘制思维导图
  • 驱动-设备树插件注册子系统
  • 【机器学习深度学习】大模型应用落地:微调与RAG的角色与实践
  • 为什么需要日志收集系统
  • 人工智能——自动微分
  • 大数据中需要知道的监控页面端口号都有哪些
  • C语言学习笔记——文件
  • 基于Python的实习僧招聘数据采集与可视化分析,使用matplotlib进行可视化
  • iptables封堵实验
  • Java——详解形参实参方法的重载
  • 《C语言》函数练习题--3
  • (易视宝)易视TV is-E4-G-全志A20芯片-安卓4-烧写卡刷工具及教程
  • Docker国内可用镜像列表(长期免费)
  • 三重移相的TPS双有源桥(DAB)变换器【simulink仿真模型】