【平衡二叉树】AVL树(双旋)

图片名称
🎉博主首页: 有趣的中国人

🎉专栏首页: C++进阶

🎉其它专栏: C++初阶 | Linux | 初阶数据结构

在这里插入图片描述

小伙伴们大家好,本片文章将会讲解AVL树左双选和右双旋的相关内容。


如果看到最后您觉得这篇文章写得不错,有所收获,麻烦点赞👍、收藏🌟、留下评论📝。您的支持是我最大的动力,让我们一起努力,共同成长!

文章目录

  • `1. 左右双旋`
  • `1. 右左双旋`
  • `3. AVL的验证`
  • `3. AVL的验证`
  • `3. AVL的性能`



1. 左右双旋


⚡出现情况
在这里插入图片描述

1. 此处在30的左子树或者右子树新增节点都会引发旋转;
2. 如果单纯的对根节点进行右单旋,并不能解决左边高的问题,会变成右边高,所以要进行双旋,步骤如下:

1. 先对parent->left节点进行左单旋

在这里插入图片描述

2. 再对根节点进行右单旋

在这里插入图片描述
完整步骤
在这里插入图片描述

我们假设顶端节点叫做parentparent->left 叫做subLsubL->right 叫做subLR


左右双旋后满足二叉搜索树的性质:

左右双旋后,实际上就是让subLR的左子树和右子树,分别作为subLparent的右子树和左子树,再让subLparent分别作为subLR的左右子树,最后让subLR作为整个子树的根。

1. subLR的左子树当中的结点本身就比subL的值大,因此可以作为subL的右子树。

2. subLR的右子树当中的结点本身就比parent的值小,因此可以作为parent的左子树。

3. 经过步骤1、2后,subL及其子树当中结点的值都就比subLR的值小,而parent及其子树当中结点的值都就比subLR的值大,因此它们可以分别作为subLR的左右子树。


左右双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:

1、subLR原始平衡因子是-1时,左右双旋后parent、subL、subLR的平衡因子分别更新为1、0、0
在这里插入图片描述


2、subLR原始平衡因子是1时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、-1、0
在这里插入图片描述


3、subLR原始平衡因子是0时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、0、0
在这里插入图片描述

代码如下:

void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;//1、以subL为旋转点进行左单旋RotateL(subL);//2、以parent为旋转点进行右单旋RotateR(parent);if (bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else if (bf == 0){subL->_bf = subLR->_bf = parent->_bf = 0;}else {assert(false);}
}


1. 右左双旋


⚡出现情况

在这里插入图片描述

1. 此处在60的左子树或者右子树新增节点都会引发旋转;
2. 如果单纯的对根节点进行左单旋,并不能解决右边高的问题,会变成左边高,所以要进行双旋,步骤如下:

1. 先对subR节点进行右单旋

在这里插入图片描述

2. 对parent节点进行左单旋

在这里插入图片描述
3. 完整步骤

在这里插入图片描述

右左双旋后满足二叉搜索树的性质:

右左双旋后,实际上就是让subRL的左子树和右子树,分别作为parent和subR的右子树和左子树,再让parent和subR分别作为subRL的左右子树,最后让subRL作为整个子树的根。

1、subRL的左子树当中的结点本身就比parent的值大,因此可以作为parent的右子树。

2、subRL的右子树当中的结点本身就比subR的值小,因此可以作为subR的左子树。

3、经过步骤1、2后,parent及其子树当中结点的值都就比subRL的值小,而subR及其子树当中结点的值都就比subRL的值大,因此它们可以分别作为subRL的左右子树。


右左双旋后,平衡因子的更新随着subRL原始平衡因子的不同分为以下三种情况:

1、subRL原始平衡因子是1时,右左双旋后parent、subR、subRL的平衡因子分别更新为-1、0、0
在这里插入图片描述


2、subRL原始平衡因子是-1时,右左双旋后parent、subR、subRL的平衡因子分别更新为0、1、0
在这里插入图片描述


3、subRL原始平衡因子是0时,左右双旋后parent、subR、subRL的平衡因子分别更新为0、0、0
在这里插入图片描述

代码如下:

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


3. AVL的验证


AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    • 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
  2. 验证其为平衡树
    • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
    • 节点的平衡因子是否计算正确

详解代码:

public:void InOrder()
{_InOrder(_root);
}int Size()
{_Size(_root);
}int Height()
{_Height(_root);
}bool IsBalanceTree()
{return _IsBalanceTree(_root);
}private:bool _IsBalanceTree(Node* root)
{if (root == nullptr){return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 计算左右子树高度差绝对值int dec = abs(leftHeight - rightHeight);// 如果比1大说明不平衡if (dec > 1){cout << root->_kv.first << endl;return false;}// 检查平衡因子是否计算正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
int _Height(Node* root)
{if (root == nullptr){return 0;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return max(leftHeight, rightHeight) + 1;
}int _Size(Node* root)
{if (root == nullptr){return 0;}return _Size(root->_left) + _Size(root->_right) + 1;
}void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);
}

3. AVL的验证


⚡验证示例1

int a[] = {16, 3, 7, 11, 9, 26, 18, 14, 15};

验证代码:

void AVLTest1()
{AVLTree<int, int> t;int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };for (auto& e : a){t.Insert({ e,e });cout << "Insert:" << e << "->" << t.IsBalanceTree() << endl;}t.InOrder();
}

在这里插入图片描述

⚡验证示例2

int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };

验证代码:

void AVLTest1()
{AVLTree<int, int> t;int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto& e : a){t.Insert({ e,e });cout << "Insert:" << e << "->" << t.IsBalanceTree() << endl;}t.InOrder();
}

在这里插入图片描述



3. AVL的性能


AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

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

相关文章

HTML常用标签-表格标签

表格标签 1 常规表格2 单元格跨行3 单元格跨行 1 常规表格 table标签 代表表格 thead标签 代表表头 可以省略不写 tbody标签 代表表体 可以省略不写 tfoot标签 代表表尾 可以省略不写 tr标签 代表一行 td标签 代表行内的一格 th标签 自带加粗和居中效果的td 代码 <h…

操作系统-页式存储(淘汰页面问题)

进程P有5个页面&#xff0c;页号为0~4&#xff0c;页面变换表及状态位、访问位和修改位的含义如下图所示。若系统给进程P分配了3个存储块&#xff0c;当访问的页面3不在内存时&#xff0c;应该淘汰表中页号为&#xff08;&#xff09;的页面。 解答&#xff1a; 被淘汰的页面首…

网上有哪些赚钱的方法能一天赚二三十?盘点7个靠谱的搞钱副业和赚钱软件

想在家里躺着就能把钱赚&#xff1f;这不再是遥不可及的梦想&#xff01;随着互联网的飞速发展&#xff0c;网上赚钱的方式层出不穷&#xff0c;总有一款适合你。 今天&#xff0c;就让我们一起揭开这些神秘面纱&#xff0c;看看哪些网上赚钱秘诀能让你轻松实现月入过万&#x…

Python 操作数据库

十、Python3 操作数据库 1、Python3 操作 MySQL 1、基本介绍 Python3 操作 MySQL 数据库 可以使用的模块是 pymysql 和 MySQLdb。 这个两个模块都是通过自己的 API 执行原生的 SQL 语句实现的。 MySQLdb 是最早出现的一个操作 MySQL 数据库的模块&#xff0c;核心由C语言编…

LLM Agent智能体综述(万字长文)

前言 &#x1f3c6;&#x1f3c6;&#x1f3c6;在上一篇文章中&#xff0c;我们介绍了如何部署MetaGPT到本地&#xff0c;获取OpenAI API Key并配置其开发环境&#xff0c;并通过一个开发小组的多Agent案例感受了智能体的强大&#xff0c;在本文中&#xff0c;我们将对AI Agent…

Linux的命名管道 共享内存

目录 命名管道 mkfifo函数 unlink函数 命名管道类 服务端 客户端 共享内存 shmget函数 ftok函数 key和shmid的区别 snprintf函数 ipcs指令 ipcrm指令 shmctl函数 shmat函数 void*做返回值 创建共享内存空间 服务端 客户端 命名管道 基本概念&#xff1…

【Shell脚本】Shell编程之数组

目录 一.数组 1.基本概念 2.定义数组的方法 2.1.方法一 2.2.方法二 2.3.方法三 2.4.方法四 2.5.查看数组长度 2.6.查看数组元素下标 3.数组分片 4.数组字符替换 4.1.临时替换 4.2.永久替换 5.数组删除 5.1.删除某个下标 5.2.删除整组 6.数组遍历和重新定义 7…

【js】获取媒体流实现拍照功能,摄像头切换

<script setup>import {onMounted,reactive,ref} from vueconst videoConstraints reactive({width: 500,height: 300});let picArr reactive([])let videoNode ref(null)let show ref(true)let stream reactive({})onMounted(async () > {// 获取视频流&#xf…

Java面试题:ConcurrentHashMap

ConcurrentHashMap 一种线程安全的高效Map集合 jdk1.7之前 底层采用分段的数组链表实现 一个不可扩容的数组:segment[] 数组中的每个元素都对应一个HashEntry数组用以存放数据 当放入数据时,根据key的哈希值找到对应的segment数组下标 找到下标后就会添加一个reentrantlo…

自动化测试框架怎么选?Robot Framework怎么搭建环境?

本系列文章跟大家分享的内容是Robot Framework从入门到实践的整个过程&#xff0c;首先会简单为大家介绍一下自动化测试框架&#xff0c;包括框架选择、环境搭建、接口自动化等&#xff0c;最后会带大家实际操作一遍&#xff0c;本文我们主要为大家介绍自动化测试框架的不同以及…

c++ 各版本特性介绍

c C是一种高级编程语言&#xff0c;以其强大的功能、灵活性和高效性而闻名。它是由Bjarne Stroustrup在20世纪80年代初期在贝尔实验室开发的&#xff0c;作为C语言的一个扩展。C不仅包含了C语言的所有特性&#xff0c;还引入了面向对象编程&#xff08;OOP&#xff09;的概念&…

Echarts结课之小杨总结版

Echarts结课之小杨总结版 前言基础回顾框架sale框架代码&#xff1a; user框架基础代码&#xff1a; inventory框架基础代码&#xff1a; total框架基础代码&#xff1a; 基础设置1.标题(Title)2.图例(Legend)实现 3.工具提示(Tooltip)实现 4.X轴(X Axis) 和 Y轴(Y Axis)5.数据…