【数据结构 07】AVL树

目录

一、二叉搜索树

二、AVL树

2.1 左单旋

2.2 右单旋

2.3 左右双旋

2.4 右左双旋

三、AVL.h

四、test.cpp


一、二叉搜索树

二叉搜索树,又称二叉排序树(Binary Search Tree),相比于普通二叉树,BST的特性有:

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

二叉搜索树通常在数据插入的时候便会将数据正确排序(不允许插入重复值),这样在进行数据查询的时候,一般情况下时间复杂度是O(\log_2 n),但是如果插入的数据为一个有序或接进有序的序列,即退化成了单支树,这时时间复杂度退化到O(n)

二、AVL树

AVL树也叫平衡二叉搜索树,是二叉搜索树的进化版,设计是原理是弥补二叉搜索树的缺陷:当插入的数据接近于有序数列时,二叉搜索树的性能严重下降。

AVL的节点设计采用三叉链结构(每个节点包含left, right, parent三个节点指针),每个节点中都有平衡因子bf。

AVL树在BST的基础上引入了一个平衡因子bf用于表示左右子树的高度差,并控制其不超过1,若在数据插入时左右子树的高度差超过1了,AVL树会寻找新的节点作为根节点进行调整树的结构,这样AVL树进行数据查询的时间复杂度就稳定在了O(\log_2 n)

AVL的特点是左子树和右子树高度差 < 2,平衡因子bf就是右子树高度 - 左子树高度的差,当bf等于2或-2时,AVL将根据不同情况进行旋转调节,使其始终保持AVL树的特性。

对于一棵AVL树,若它的节点数为n,则它的深度为\log_2 n

2.1 左单旋

若数列 1,2,3 按顺序插入AVL树,则树的结构为图1:

图1 数列{1,2,3}插入AVL树

图1 中,parent节点的平衡因子bf为2,新节点向上调节时的cur节点的平衡因子为1,这种情况需要左单旋:cur节点的左子树(空)变成parent节点的右子树,parent节点变成cur节点的左子树。

图2 左单旋后的节点

2.2 右单旋

右单旋与左单旋类似,也是根据平衡因子情况进行旋转调整。

图1 向AVL树中插入新节点1

图1 新节点1插入,向上调整平衡因子,出现parent->bf == -2, cur->bf == -1,此时需要右单旋:cur节点的右子树变为parent节点的左子树,parent变成cur节点的右子树。

图2 右单旋结果

2.3 左右双旋

左右双旋在设计上可以调用左单旋和右单旋函数,但是需要不同情况讨论旋转后的平衡因子。

图1 新插入节点0

图1 新插入节点0后,parent->bf == -2, cur->bf == 1,此时需要左右双旋,即先以节点-1为父节点进行一次左单旋,再以1为父节点进行一次左单旋:节点0的左子树(空)变成节点-1的左子树,节点-1变成节点0的左子树。

图2 以节点-1位父节点左单旋结果

图2 完成左单旋之后再以1位父节点进行一次右单旋:节点0的右子树(空)变成节点1的左子树,节点1变成节点0的右子树。

图3 完成右单旋,同时完成左右双旋

2.4 右左双旋

图1 插入新节点65

图1 中红色数字就是每个节点平衡因子,值为65的节点是新插入的节点,当其插入之后,所有节点的平衡因子更新,出现了parent平衡因子为2,cur平衡因子为1,此时需要进行旋转调节。

图2 观察平衡因子

图2 观察平衡因子,决定旋转策略为右左双旋,即先进行一次右单旋,再进行一次左单旋。右单旋是以节点80为父节点进行,即节点60的右子树变成节点80的左子树,节点80变成节点60的右子树。

图3 右单旋结果

图3 右单旋结束,接下来再一次进行以节点40为父节点的左单旋,即节点60的左子树变成节点40的右子树,节点40变成节点60的左子树。

图4 右左双旋旋转最终结果

三、AVL.h

#define _CRT_SECURE_NO_WARNINGS 1#pragma once
#include <iostream>template<class K, class V>
struct AVLTreeNode
{std::pair<K, V> kv;AVLTreeNode* parent;AVLTreeNode* left;AVLTreeNode* right;int bf;AVLTreeNode(const std::pair<K, V> x): kv(x), parent(nullptr), left(nullptr), right(nullptr), bf(0){}
};template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:bool Insert(const std::pair<K, V>& x){if (_root == nullptr){_root = new Node(x);return true;}// 通过二叉搜索树找到需要插入节点的位置Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (cur->kv.first < x.first)cur = cur->right;else if (cur->kv.first > x.first)cur = cur->left;elsereturn false;}cur = new Node(x);cur->parent = parent;if (parent->kv.first > x.first)parent->left = cur;elseparent->right = cur;// 更新平衡因子while (parent){if (parent->left == cur)--parent->bf;else++parent->bf;if (parent->bf == 0){return true;}else if (parent->bf == 1 || parent->bf == -1){// 继续向上更新cur = parent;parent = parent->parent;}else if (parent->bf == 2 || parent->bf == -2){if (parent->bf == 2 && cur->bf == 1)_RotateLeft(parent);else if (parent->bf == -2 && cur->bf == -1)_RotateRight(parent);else if (parent->bf == -2 && cur->bf == 1)_RotateLeftRight(parent);else if (parent->bf == 2 && cur->bf == -1)_RotateRightLeft(parent);break;}}}void InOrder(){_InOrder(_root);std::cout << std::endl;}bool IsBalance(){return _IsBalance(_root);}private:void _RotateLeft(Node* parent){Node* subR = parent->right;Node* subRL = subR->left;parent->right = subRL;if (subRL != nullptr)subRL->parent = parent;subR->left = parent;Node* ppNode = parent->parent;parent->parent = subR;if (ppNode == nullptr){_root = subR;_root->parent = nullptr;}else{if (ppNode->left == parent)ppNode->left = subR;elseppNode->right = subR;subR->parent = ppNode;}parent->bf = subR->bf = 0;}void _RotateRight(Node* parent){Node* subL = parent->left;Node* subLR = subL->right;parent->left = subLR;if (subLR != nullptr)subLR->parent = parent;subL->right = parent;Node* ppNode = parent->parent;parent->parent = subL;if (ppNode == nullptr){_root = subL;_root->parent = nullptr;}else{if (ppNode->left == parent)ppNode->left = subL;elseppNode->right = subL;subL->parent = ppNode;}parent->bf = subL->bf = 0;}void _RotateLeftRight(Node* parent){Node* subL = parent->left;Node* subLR = subL->right;int bf = subLR->bf;_RotateLeft(subL);_RotateRight(parent);if (bf == 0){subLR->bf = 0;subL->bf = 0;parent->bf = 0;}else if (bf == -1){subLR->bf = 0;subL->bf = 0;parent->bf = 1;}else if (bf == 1){subLR->bf = 0;subL->bf = -1;parent->bf = 0;}}void _RotateRightLeft(Node* parent){Node* subR = parent->right;Node* subRL = subR->left;int bf = subRL->bf;_RotateRight(subR);_RotateLeft(parent);if (bf == 0){subRL->bf = 0;subR->bf = 0;parent->bf = 0;}else if (bf == -1){subRL->bf = 0;subR->bf = 1;parent->bf = 0;}else if (bf == 1){subRL->bf = 0;subR->bf = 0;parent->bf = -1;}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->left);std::cout << "<" << root->kv.first << "," << root->kv.second << ">" << " ";_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;}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->left);int rightHeight = _Height(root->right);if (rightHeight - leftHeight != root->bf){std::cout << "平衡因子异常" << std::endl;return false;}return abs(rightHeight - leftHeight) < 2&& _IsBalance(root->left)&& _IsBalance(root->right);}private:Node* _root = nullptr;
};

四、test.cpp

#define _CRT_SECURE_NO_WARNINGS 1#include "AVL.h"void Test1()
{int a[] = { 2, 4, 5, 8, 10, 1, 3, 5, 7, 9, 100, 200, -100, 0 };AVLTree<int, int> t;for (auto e : a){t.Insert(std::make_pair(e, e * 2));}t.InOrder();std::cout << t.IsBalance() << std::endl << std::endl;
}void Test2()
{int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };AVLTree<int, int> t;for (auto e : a){t.Insert(std::make_pair(e, e * 2));}t.InOrder();std::cout << t.IsBalance() << std::endl << std::endl;
}void Test3()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int, int> t;for (auto e : a){t.Insert(std::make_pair(e, e * 2));}t.InOrder();std::cout << t.IsBalance() << std::endl << std::endl;
}int main()
{Test1();Test2();Test3();return 0;
}

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

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

相关文章

UE5 C++ 读取本地图片并赋值到UI上

目录 结果图 节点样式 主要代码 调试代码 结果图 节点样式 主要代码 &#xff08;注释纯属个人理解&#xff0c;可能存在错误&#xff09; // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h&q…

rocketmq自定义开机自启动【systemctl方式】

环境&#xff1a; rocketmq版本&#xff1a;4.7.0 rocketmq二进制包&#xff1a;rocketmq-all-4.7.0-bin-release.zip 系统&#xff1a;linux系统 1. 准备namesrv启动脚本 mqnamesrv.sh【任意路径】 #!/bin/bash source /etc/profile workdir"/home/ec2-user/config/…

Java面向对象详解

面向对象和面向过程的区别&#xff1a; 面向对象和面向过程都是对软件分析、设计和开发的一种思想&#xff0c;它指导着人们以不同的方式去分析、设计和开发软件。C语言是一种典型的面向过程语言&#xff0c;Java是一种典型的面向对象语言。 面向过程适合简单、不需要协作的事务…

Flask和Go框架相比

Flask和Go框架是目前非常受欢迎的Web开发框架&#xff0c;它们在设计和目标上有所不同。 首先&#xff0c;Flask是一个轻量级的Python Web框架&#xff0c;而Go框架是基于Go语言的。 Flask注重简单性和灵活性&#xff0c;提供了一些基本的工具和功能&#xff0c;使得开发者能够…

C#的String.Format 方法

目录 一、重载 二、Format(String, Object) 1.定义 2.示例 三、Format(String, Object[]) 1.定义 2.示例 3.示例 将对象的值转换为基于指定格式的字符串&#xff0c;并将其插入到另一个字符串。 一、重载 Format(IFormatProvider, String, Object, Object, Object) …

PDF操作——批量删除末页

一、说明 由于PDF末页为空白页或者是免责声明需要删除&#xff0c;涉及的文件比较多&#xff0c;因此写了一小段代码进行处理。 二、完整架构流程 这个代码的整体架构流程可以分为以下几个步骤&#xff1a; 导入所需的库&#xff1a;首先&#xff0c;代码导入了PyPDF2和os两…

Optimism的挑战期

1. 引言 前序博客&#xff1a; Optimism的Fault proof 用户将资产从OP主网转移到以太坊主网时需要等待一周的时间。这段时间称为挑战期&#xff0c;有助于保护 OP 主网上存储的资产。 而OP测试网的挑战期仅为60秒&#xff0c;以简化开发过程。 2. OP与L1数据交互 L1&#xf…

探索智能巡检机器人深度学习的奥秘

机器人深度学习&#xff08;Robot Deep Learning&#xff09;是指利用深度学习技术&#xff0c;使机器人能够从大量数据中学习和提取特征&#xff0c;进而实现自主感知、决策和行动的能力。通过深度学习算法&#xff0c;机器人可以从传感器获取的数据中自动学习模式和规律&…

微信开放平台第三方开发,实现代小程序认证申请

大家好&#xff0c;我是小悟 微信小程序认证整体流程总共分为五个环节&#xff1a;认证信息填写、平台初审、管理员验证、供应商审核和认证成功。 服务商可以代小程序发起认证申请。平台将对认证基础信息进行初步校验。通过后&#xff0c;平台将向管理员微信下发模板消息&…

Redis(十)SpringBoot集成Redis

文章目录 连接单机mvnYMLController.javaRedisConfig.java 连接集群YML问题复现 RedisTemplate方式 连接单机 mvn <!--Redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</art…

SRC实战 | 信息泄露挖掘

本文由掌控安全学院 - 叴龙 投稿 1. 信息搜集 首先老语法先搜集一波&#xff0c;毕竟没有钓鱼和sg的能力&#xff0c;只能找注册站去挖挖了。 web.title”XX大学”&&web.body”忘记密码”&&web.body”注册” 2. 漏洞挖掘 这里找到一个可以注册网站接口&…

蓝桥杯 第 1 场 小白入门赛

目录 1.蘑菇炸弹 2.构造数字 3.小蓝的金牌梦 4.合并石子加强版 5.简单的LIS问题 6.期望次数 1.蘑菇炸弹 我们直接依照题目 在中间位置的数进行模拟即可 void solve(){cin>>n;vector<int> a(n1);for(int i1;i<n;i) cin>>a[i];int ans0;for(int i2;i…

WGS 84框架发布新版本-WGS 84(G2296)

根据NGA发布的最新消息&#xff0c;WGS 84框架发布了新版本-WGS 84(2296)&#xff0c;上一版本是G2139。以下是相关信息。 WGS 84 TERRESTRIAL REFERENCE FRAME (TRF) NGA implemented a new realization of the WGS 84 TRF, named WGS 84 (G2296) in its orbit and clock pr…

XSS haozi靶场通关笔记

XSS靶场地址&#xff1a;alert(1) 靶场的要求是输出一个内容为1的弹窗&#xff1b;这个靶场限制了输入位置只能是input code&#xff1b;而且浏览器发送内容时会自动进行url编码&#xff1b;所以重点考察的是代码的分析和基础payload构造&#xff1b;一切完成在当前页面&#…

【数据结构】链表(单链表实现+测试+原码)

1.链表 1.1 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 现实中&#xff1a;链表就像是一列动车&#xff0c;一节连着一节 数据结构中的链表 注意: 1.从上图可看出…

解决php date()输出时间与Linux系统不一致

一、在Docker容器创建好之后&#xff0c;可能会发现容器时间跟宿主机时间不一致 我们需要同步它们的时间&#xff0c;让容器时间跟宿主机时间保持一致。有以下几种方法&#xff1a; 方法1&#xff1a; 在创建 Docker 容器时&#xff0c;加上 “-v /etc/localtime:/etc/local…

python管理依赖关系库

一、poetry Poetry 是一个用于管理 Python 项目依赖关系和构建工具的工具。它提供了一个简单的命令行界面&#xff0c;可以帮助您创建、管理和发布 Python 项目&#xff0c;使用方法&#xff1a;command [options] [arguments] 官网&#xff1a;https://python-poetry.org/doc…

React16源码: React中LegacyContext的源码实现

LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了&#xff0c;就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢&#xff1f;因为它…

知识点积累系列(一)golang语言篇【持续更新】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 知识点积累 系列文章的第一篇&#xff0c;记录golang语言相关的知识点 1.结构体的mapstructure是什么 mapstructure:"default" mapstructure是一个Go语言的库&#xff0c;用于将一个map中的值映射到…

C语言王道第八周一题

Description 初始化顺序表&#xff08;顺序表中元素为整型&#xff09;&#xff0c;里边的元素是 1,2,3&#xff0c;然后通过 scanf 读取一个元素&#xff08;假如插入的是 6&#xff09;&#xff0c;插入到第 2 个位置&#xff0c;打印输出顺序表&#xff0c;每个 元素占 3 个…