【数据结构 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…

Java面向对象详解

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

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…

XSS haozi靶场通关笔记

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

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

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

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 个…

添加了gateway之后远程调用失败

前端提示500&#xff0c;后端提示[400 ] during [GET] to [http://userservice/user/1] 原因是这个&#xff0c;因为在请求地址写了两个参数&#xff0c;实际上只传了一个参数 解决方案&#xff1a;加上(required false)并重启所有相关服务

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson13(买东西)(餐厅点餐事宜;询问有无座位;食物如何调理:牛排、咖啡等;菜单等相关)

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 13 At the Restaurant 在餐厅会话A会话B笔记餐厅询问有无座位&#xff1b;餐厅电话订座其他餐厅询问有无座位的问法 吸烟区与非吸烟区&#xff08;smo…

[力扣 Hot100]Day18 矩阵置零

题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 出处 思路 在原数组上直接操作势必会出现“冗余”的0&#xff0c;即原本[i,j]处不是0&#xff0c;例如由于i行的其他位置有0导致[i,j]…

Shell中的AWK

1.awk的工作原理 逐行读取文本&#xff0c;默认以空格或tab键为分隔符进行分隔&#xff0c;将分隔所得的各个字段保存到内建变量中&#xff0c;并按模式或者条件执行编辑命令。awk倾向于将一行分成多个"字段"然后再进行处理。awk信息的读入也是逐行读取的&#xff0…

day38_MySQL

今日内容 0 复习昨日 1 引言 2 数据库 3 数据库管理系统 4 MySQL 5 SQL语言 0 复习昨日 1 引言 1.1 现有的数据存储方式有哪些&#xff1f; Java程序存储数据&#xff08;变量、对象、数组、集合&#xff09;&#xff0c;数据保存在内存中&#xff0c;属于瞬时状态存储。文件&…

删除有序数组中的重复项[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个非严格递增排列的数组nums&#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素只出现一次&#xff0c;返回删除后数组的新长度。元素的相对顺序应该保持一致。然后返回nums中唯一元素的个数。 考虑nums的唯一元素…

Source Insight 4的使用经验

问题1 函数结束提示符<<end tagDebugData>> 在source insight里的options→preference→syntax decorations→auto annotations→annotate closing braceswith end-comments关闭或启动显示. 但是有的有有的没有&#xff0c;奇怪! 参照网络答案&#xff1a; Sou…