数据结构:AVL树

目录

1、AVL树的概念

2、二叉搜索树的功能与实现

1、AVL树节点定义

2、AVL树的插入

3、AVL树的旋转操作

1、左旋

2、右旋

3、左右旋

 4、右左旋

 3、AVL树完整代码实现


1、AVL树的概念

        在前面的文章中,我们学过了二叉搜索树,二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是 AVL
左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1)
平衡因子:右子树高度减去左子树高度
如果一棵二叉搜索树是高度平衡的,它就是 AVL 树。如果它有 n 个结点,其高度可保持在
O(log2 n) ,搜索时间复杂度 O(log2 n)
以下是一个简单AVL树的示例:节点周围的是平衡因子。

2、二叉搜索树的功能与实现

1、AVL树节点定义

template<class K,class V>struct AVLTreeNode{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; //平衡因子pair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0),_kv(kv){}};
每个节点的平衡因子设置为0,每个节点包含左右指针,以及父节点指针,每个节点的数据用pair来实现,第一个元素为Key来比较。

2、AVL树的插入

AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看作二叉搜索树

插入过程与二叉搜索树一致,只不过要注意更新节点的平衡因子。

因为平衡因子是右子树高度减去左子树高度,所以如果在左子树添加节点,bf(平衡因子)--,在右子树添加,bf++。

parent节点的平衡因子更新有三种情况:

1、parent的平衡因子为0,说明满足AVL性质,插入成功。

2、parent的平衡因子为1或-1,说明该树的高度增加,需要向上更新。

3、parent的平衡因子为-2或2,说明该节点违反了平衡树性质,需要对其进行旋转处理。

旋转处理下面会介绍,我们先来看插入的代码实现:

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--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = 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){RotateLR(parent);}else{RotateRL(parent);}break;}else{assert(false);}}return true;}

寻找插入位置与搜索二叉树一致,然后更新平衡因子 ,对于不同的违反AVL树性质需要不同的旋转操作。

3、AVL树的旋转操作

我们先来看左旋对应的情况:

1、左旋

a,b,c具有相同的高度,旋转后注意更新parent和cur的平衡因子为0;
void RotateL(Node* parent){Node* sub = parent->_right;Node* subl = sub->_left;parent->_right = subl;if (subl){subl->_parent = parent;}sub->_left = parent;Node* ppnode = parent->_parent;parent->_parent = sub;if (parent == _root){_root = sub;sub->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = sub;}else{ppnode->_right = sub;}sub->_parent = ppnode;}parent->_bf = 0;sub->_bf = 0;}

2、右旋

原理与左旋相似,不过是向右旋转而已 (a,b,c具有相同的高度)

void RotateR(Node* parent){Node* sub = parent->_left;Node* subr = sub->_right;parent->_left = subr;if (subr){subr->_parent = parent;}sub->_right = parent;Node* ppnode = parent->_parent;parent->_parent = sub;if (parent == _root){_root = sub;sub->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = sub;}else{ppnode->_right = sub;}sub->_parent = parent;}parent->_bf = 0;sub->_bf = 0;}

3、左右旋

先以subl为根左旋,再以parent为根进行右旋。(a,b,c具有相同的高度)

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

根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。

 4、右左旋

原理与左右旋相似,只是换了个方向。(a,b,c具有相同的高度)

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

根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。 

 3、AVL树完整代码实现

内部包含查找以及判断是否是AVL树的函数,以及中序遍历。

#pragma once
namespace AVLTree_test
{template<class K,class V>struct AVLTreeNode{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; //平衡因子pair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0),_kv(kv){}};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--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = 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){RotateLR(parent);}else{RotateRL(parent);}break;}else{assert(false);}}return true;}void RotateL(Node* parent){Node* sub = parent->_right;Node* subl = sub->_left;parent->_right = subl;if (subl){subl->_parent = parent;}sub->_left = parent;Node* ppnode = parent->_parent;parent->_parent = sub;if (parent == _root){_root = sub;sub->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = sub;}else{ppnode->_right = sub;}sub->_parent = ppnode;}parent->_bf = 0;sub->_bf = 0;}void RotateR(Node* parent){Node* sub = parent->_left;Node* subr = sub->_right;parent->_left = subr;if (subr){subr->_parent = parent;}sub->_right = parent;Node* ppnode = parent->_parent;parent->_parent = sub;if (parent == _root){_root = sub;sub->_parent = nullptr;}else{if (parent == ppnode->_left){ppnode->_left = sub;}else{ppnode->_right = sub;}sub->_parent = parent;}parent->_bf = 0;sub->_bf = 0;}void RotateLR(Node* parent){Node* subl = parent->_left;Node* sublr = subl->_right;int bf = sublr->_bf;RotateL(subl);RotateR(parent);if (bf == -1){sublr->_bf = 0;parent->_bf = 1;subl->_bf = 0;}else if (bf == 1){sublr->_bf = 0;parent->_bf = 0;subl->_bf = -1;}else if (bf == 0){subl->_bf = 0;parent->_bf = 0;sublr->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent){Node* subr = parent->_right;Node* subrl = subr->_left;int bf = subrl->_bf;RotateR(subr);RotateL(parent);if (bf == -1){subrl->_bf = 0;parent->_bf = 0;subr->_bf = 1;}else if (bf == 1){subrl->_bf = 0;parent->_bf = -1;subr->_bf = 0;}else if(bf==0){subrl->_bf = 0;parent->_bf = 0;subr->_bf = 0;}else{assert(false);}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " " << root->_bf << endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);}int Height(Node* root){if (root == nullptr){return 0;}int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);if (abs(rightHeight - leftHeight) >= 2){cout << root->_kv.first << "不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return _IsBalance(root->_left) && _IsBalance(root->_right);}bool IsBalance(){return _IsBalance(_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 NULL;}private:Node* _root = nullptr;};void TestAVLTree1(){int a[] = { 4, 2, 6, 1,0 ,67,56,33,212,90};AVLTree<int, int> t;for (auto e : a){if (e == 14){int x = 0;}t.Insert(make_pair(e,e));}t.InOrder();cout << t.IsBalance();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/727782.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

勾股定理的七种经典证明

据说勾股定理约有500种证明方法&#xff0c;下面介绍几种经典的证明方法。 一、切割重拼法。 顾名思义&#xff0c;就是将图形切割成其他形式的图形&#xff0c;然后通过拼图转换为另一种图形&#xff0c;这个过程中图形的面积是不变的。 “赵爽弦图”是这种方法的经典应用&…

Android视角看鸿蒙第三课(module.json中的各字段含义之nametype)

Android视角看鸿蒙第三课(module.json中的各字段含义) 前言 上篇文章我们试图找到鸿蒙app的程序入口&#xff0c;确定了在鸿蒙工程中,由AppScope下的app.json5负责应用程序的图标及名称,由entry->src->main-module.json5负责桌面图标及名称的展示。 AppScope下的app.js…

使用Kali搭建钓鱼网站教程

一、前言 使用kali工具一分钟制作出和目标网站一模一样的钓鱼网站。目标用户使用钓鱼网站登录自己的账号&#xff0c;账号密码将被自动劫持。 二、钓鱼网站的制作过程 1.在虚拟机VMvare中登录kali linux 2.准备一个目标网址 3.在kail中搜索使用工具 4.在弹出的选项中选择第一…

【JavaEE初阶】 JVM简介

文章目录 &#x1f38d;前言&#x1f343;JVM发展史&#x1f6a9;Sun Classic VM&#x1f6a9;Exact VM&#x1f6a9;HotSpot VM&#x1f6a9;JRockit&#x1f6a9;J9 JVM&#x1f6a9;Taobao JVM&#xff08;国产研发&#xff09; &#x1f340;JVM 运行流程⭕总结 &#x1f3…

基于毕奥-萨伐尔定律的交流电机的4极旋转磁场matlab模拟与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于毕奥-萨伐尔定律的交流电机的4极旋转磁场&#xff0c;对比不同定子半径&#xff0c;对比2级旋转磁场。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB2022a…

RK3568平台 USB数据包的收发格式

一.USB硬件拓扑结构 compound device &#xff1a;多个设备组合起来&#xff0c;通过HUB跟Host相连composite device &#xff1a;一个物理设备有多个逻辑设备(multiple interfaces) 在软件开发过程中&#xff0c;我们可以忽略Hub的存在&#xff0c;硬件拓扑图简化如下&#x…

【开源物联网平台】使用MQTT.fx模拟设备接入FastBee物联网平台

​&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、接入步骤 1.1 创建产品&#xff…

9款世界级垂直领域软件架构师Visio平替作图工具!

1 LucidChart 一个基于HTML5的在线流程图绘制和协作应用平台&#xff0c;用户可以通过它方便快速的实现流程图表的绘制&#xff0c;同时还可以实现与他人进行实时的流程图绘制和修改功能&#xff0c;对需要群组协作功能的团队来说&#xff0c;这点非常方便。 由于LucidChart是…

VM内存结构和垃圾回收机制

引言 在计算机科学中&#xff0c;虚拟机&#xff08;VM&#xff09;是一个重要的概念&#xff0c;它允许程序在硬件平台之上运行。虚拟机模拟真实机器的行为&#xff0c;为程序提供了一个独立的运行环境。本文将深入探讨VM的内存结构和垃圾回收机制&#xff0c;以帮助读者更好…

图形系统开发实战课程:进阶篇(上)——10.应用实例:交通路网

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形样式”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-10.html 第十章 应用实例&#xff1a;交通路网 \quad 在前面几…

基于springboot精品在线试题库系统论文

摘 要 使用旧方法对作业管理信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在作业管理信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发的精品在线试题库系…

python爬虫(2)

继上节 查看数组维数 可以使用数组的ndim属性 代码示例如下&#xff1a; import numpy as np c np.random.randint(1,9,5) print(c.ndim) 结果如下&#xff1a; 当然这些也可以结合前面的各种用法来使用 1、选取数组元素 &#xff08;1&#xff09;一维数组的元素…

线程池不香了? 结构化并发才是王道!

我们先定义获取用户信息任务&#xff1a; 再定义获取订单信息任务&#xff1a; 然后再构造线程池并执行任务&#xff1a; 输出结果为&#xff1a; 看上去一切都刚刚好&#xff0c;但是&#xff0c;如果获取订单信息时出错了&#xff0c;此时会是什么现象呢&#xff1f;修改获取…

外包干了一周,技术明显倒退。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了南京一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

BUUCTF-Misc2

wireshark1 1.打开附件 发现是流量包&#xff0c;放到Wireshark中分析 2.过滤 根据题目的提示寻找管理员登录的网站&#xff0c;从中获取密码 用http.request.methodPOST&#xff0c;过滤当前的 HTTP 请求为 POST 方法 3.查找 双击过滤后的流量包&#xff0c;查找管理员密码…

TCP/IP模型中网络层和网络接口层的区别 通俗解释

问题 TCP/IP模型中的网络层和网络接口层有什么区别&#xff0c;或者说 ip地址和mac地址有什么区别&#xff0c;通过ip不就能找到要发送的设备了吗 为什么还需要mac地址用简单的语言来解释一下。 TCP/IP模型中的网络层和网络接口层主要的区别在于它们处理的信息和功能不同&…

Self-evolve——基于大语言模型的代码演进框架

导语 本研究提出了一个名为Self-evolve的框架&#xff0c;它旨在通过大型语言模型&#xff08;LLMs&#xff09;实现代码生成的进化。这一框架在Text-to-Code任务中引入了一种全新的处理流程&#xff0c;以提高LLMs在代码生成方面的效率和准确性。在之前&#xff0c;尽管LLMs在…

2024【问题解决】Github 2024无法克隆git clone自从签了2F2安全协议之后

项目场景:ping通Github但没法clone–502 问题描述 提示:ping通Github但没法clone--502: 例如:git clone https://gitclone.com/l.git/*** $ git clone https://github.com/darrenpig/Yocto Cloning into Yocto_tutorial... fatal: unable to access https://gitclone.co…

从仓储管理看3C电子行业智慧物流的优势

仓储管理是智慧物流的重要组成部分。通过引入自动化、智能化的仓储管理系统&#xff0c;3C电子企业可以实现库存的精准管理、快速分拣和高效配送。这不仅减少了库存成本&#xff0c;还大大提高了运营效率和市场响应速度。 传统的仓储管理依赖于人工操作和纸质文档记录&#xff…

vcomp140.dll丢失如何修复,5种修复方法轻松搞定vcomp140.dll问题

vcomp140.dll文件的丢失可能会引发一系列系统运行与软件功能上的问题。具体来说&#xff0c;这个动态链接库文件是Visual C Redistributable的一部分&#xff0c;对于许多基于此环境开发的应用程序至关重要。一旦缺失&#xff0c;可能会导致部分应用程序无法正常启动或运行&…