数据结构: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.hqwc.cn/news/529758.html

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

相关文章

【深度学习】换脸新科技,InstantID: Zero-shot Identity-Preserving Generation in Seconds

论文&#xff1a;https://arxiv.org/abs/2401.07519 代码:https://github.com/InstantID/InstantID demo&#xff1a;https://huggingface.co/spaces/InstantX/InstantID 文章目录 1 引言2 相关工作2.1 文本到图像扩散模型2.2 主题驱动的图像生成2.3 保持ID的图像生成 3 方法3.…

代码背后的女性:突破性别壁垒的技术先驱

个人主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《程序人生》 引言 在计算机科学的历史长河中&#xff0c;有许多杰出的女性为这个领域的发展做出了重要贡献。她们不仅在技术上取得了卓越成就&#xff0c;还打破了性别壁垒&#xff0c;为后来的女性树立了榜样。今…

零基础自学C语言|自定义类型:结构体

✈结构体类型的声明 前面我们在学习操作符的时候&#xff0c;已经学习了结构体的知识&#xff0c;这里稍微复习一下。 &#x1f680;结构体回顾 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 &#x1fa82;结构的声明 例如&a…

【C++】了解一下STL

个人主页 &#xff1a; zxctscl 如有转载请先通知 STL 1. 什么是STL2. STL的版本3. STL的六大组件4. STL的重要性5. 如何学习STL6. STL的缺陷 1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件…

最迟但到的 Star History 2023 年度开源精选!

千呼万唤始出来&#xff0c;Star History 2023 年终开源精选来啦&#xff01;&#x1f389; AI 是 2023 开源领域里最主要的关键词&#xff0c;但其实过去一年还是有很多其他值得关注的项目和发展趋势的&#xff01;Star History 小编总结了几个类别并精选了类别中最亮眼的项目…

MT6771 android13 自定义背光曲线

一. Android系统源码中的参数配置 MTK6771平台MTK重写了背光曲线的参数&#xff0c;路径在s0_vnd/vendor/mediatek/proprietary/packages/overlay/vendor/FrameworkResOverlayExt/brightness_adaptive_support/res/values/config.xml 不过MTK的其他平台可能不是在这个路径 来看…

学习Java的第六天

目录 一、变量 1、变量的定义 2、变量的声明格式 3、变量的注意事项 4、变量的作用域 二、常量 三、命名规范 Java 语言支持如下运算符&#xff1a; 1、算术运算符 解析图&#xff1a; 示例&#xff1a; 2、赋值运算符 解析图&#xff1a; 示例&#xff1a; 3、关…

#微信小程序创建(获取onenet平台数据)

1.IDE&#xff1a;微信开发者工具 2.实验&#xff1a;创建一个小程序&#xff08;http get获取onenet平台数据&#xff09; 3.记录&#xff1a; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1eOd-2EnilnhPWoGUMj0fzw 提取码: 2023 &#xff08;1&#xff09;新建一个工…

基于FPGA加速的bird-oid object算法实现

导语 今天继续康奈尔大学FPGA 课程ECE 5760的典型案例分享——基于FPGA加速的bird-oid object算法实现。 &#xff08;更多其他案例请参考网站&#xff1a; Final Projects ECE 5760&#xff09; 1. 项目概述 项目网址 ECE 5760 Final Project 模型说明 Bird-oid object …

Linux安全--为Nginx加上PHP解析功能

yum install php-fpm -y安装php进程管理器 找到Nginx安装的路径 编辑Nginx配置文件

短视频,文案素材哪里找?找短视频文案素材指南

有很多创业者&#xff0c;耗费了很长时间创作出了很多短视频&#xff0c;文案素材没写好&#xff0c;导致浏览量下降&#xff0c;或者写文案的同时就已经花费了很长时间&#xff0c;导致没有时间发布视频&#xff0c;从而影响了流量&#xff0c;导致流量下滑&#xff0c;其实我…

STM32CubeMX学习笔记18——FSMC(TFT-LCD屏触摸)

1.触摸屏简介 目前最常用的触摸屏有两种&#xff1a;电阻式触摸屏和电容式触摸屏 1.1 电阻式触摸屏 电阻式的触摸屏结构如下图示&#xff0c;它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成&#xff0c;这些结构层都是透明的&#xff0c;整个触摸屏覆盖在液晶面板上…