C++学习笔记:AVL树

AVL树

  • 什么是AVL树?
  • AVL树节点的定义
  • AVL树的插入
    • 平衡因子调整
    • 旋转调整
    • 左旋转
    • 右旋转
    • 左右双旋
    • 右左双旋
  • AVL树完整代码实现

什么是AVL树?

AVL是1962年,两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis 为了解决如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下的问题 ,因此发明了一种特殊的二叉搜索树,并以他们的名字命名为AVL树.
相比于普通的二叉树来说,AVL树的节点定义多了一个平衡因子:
平衡因子 = 右子树的高度 - 左子树的高度
因为AVL树需要通过平衡因子来确保它的特性,而AVL树的特性有以下几点:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述

因为有上面两点的限制,当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)

AVL树节点的定义

因为在AVL树中,若插入一些节点需要对AVL树的节点进行旋转调整等,在进行旋转调整的时候需要记录当前节点父节点的位置,因此在AVL树的节点定义如下:

template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data = T()): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _left;  AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;T _data;int _bf;   // 节点的平衡因子
};

平衡因子_bf应初始化为0

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。
但是AVL树插入值的时候会有平衡因子的改变,那么AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

平衡因子调整

  • 新增节点在父节点的左边 bf–

  • 新增节点在父节点的右边 bf++
    在更新后:

  • 插入节点之前,父亲的bf 为 1 或者 -1 ,插入节点在低的子树那边,那么更新后父亲的 bf == 0 ,左右子树高度不变,插入结束
    在这里插入图片描述

  • 插入前父节点的bf = 0,更新后父亲的 bf == 1 || bf == -1 ,父亲所在子树高度变了,需要继续往上更新
    在这里插入图片描述

  • 父亲节点bf更新后为 2 或者 -2,则说明需要进行调整
    在这里插入图片描述

新增节点可能会影响祖先,因此插入节点后,需要查看子树的高度是否变化:

  • 若子树高度不变,就不会影响祖先
  • 若子树高度改变,就会影响祖先

如下图,虽然增加了一个节点,但是并不影响节点 7 这颗子树的高度,因此祖先节点5就不会改变
在这里插入图片描述
但是出现下面这种情况,就需要进行调整了
在这里插入图片描述
因为新插入的节点影响了父节点 9 的高度,进而影响了 8 节点的平衡因子变成了2
这种时候就需要进行调整了

旋转调整

AVL树正式因为有旋转调整这一必杀利器,因此才能保证它一直是AVL树,而旋转调整又分为:左旋转,右旋转,左右旋转,右左旋转
四种旋转分别对应4种不同的情况接下来看看这些旋转的条件及思想
而旋转的必要条件就是父亲节点的bf == -2 || bf == 2:parent->_bf == -2 || parent->_bf == 2

左旋转

左旋转的条件: parent->_bf == 2 && cur->_bf == 1
如图:
简易版:
在这里插入图片描述

综合版
在这里插入图片描述

代码实现:

// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* pparent = parent->_parent;parent->_parent = subR;if (subRL){subRL->_parent = parent;}if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}subR->_parent = pparent;}parent->_bf = subR->_bf = 0;}

右旋转

右旋转的条件:parent->_bf == -2 && cur->_bf == -1
思想:
简易版:
在这里插入图片描述
综合版
在这里插入图片描述

左右双旋

左右双旋的条件:parent->_bf == -2 && cur->_bf == 1
思想:
在这里插入图片描述
将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。
代码实现

// 左右双旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);//此节点本身就是插入节点if (bf == 0){parent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == 1) //右子树的高度较高{parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == -1) //左子树的高度较高{parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}

右左双旋

右左双旋的条件:parent->_bf == 2 && cur->_bf == -1
思想:
在这里插入图片描述
右左双旋代码实现:

// 右左双旋void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){//subRL 自己就是新增节点parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == -1){//subRL 的左子树新增节点parent->_bf = 0;subR->_bf = 0;subRL->_bf = 1;}else if (bf == 1){//subRL 的右子树新增节点 parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}

AVL树完整代码实现

此代码种包含了AVL树平衡的判断和中序遍历

#pragma once
#include<iostream>
#include<assert.h>using namespace std;template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data = T()): _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _left;AVLTreeNode<T>* _right;AVLTreeNode<T>* _parent;T _data;int _bf;   // 节点的平衡因子
};// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:// 在AVL树中插入值为data的节点bool Insert(const T& data){if (_root == nullptr){_root = new Node(data);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_data > data){parent = cur;cur = cur->_left;}else if (cur->_data < data){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(data);if (parent->_data > data){parent->_left = cur;}else{parent->_right = 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 = 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){RotateRL(parent);}//左边的右子树高  左右双旋else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}//旋转完成后不再需要更新break;}else{assert(false);}}return true;}bool Isbalance(){return _Isbalance(_root);}void InOrder(){_InOrder(_root);cout << endl;}private:// 右单旋void RotateR(Node* parent){Node* pparent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;parent->_parent = subL;subL->_right = parent;if (subLR){subLR->_parent = parent;}if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}subL->_parent = pparent;}parent->_bf = subL->_bf = 0;}// 左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* pparent = parent->_parent;parent->_parent = subR;if (subRL){subRL->_parent = parent;}if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}subR->_parent = pparent;}parent->_bf = subR->_bf = 0;}// 右左双旋void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){//subRL 自己就是新增节点parent->_bf = subR->_bf = subRL->_bf = 0;}else if (bf == -1){//subRL 的左子树新增节点parent->_bf = 0;subR->_bf = 0;subRL->_bf = 1;}else if (bf == 1){//subRL 的右子树新增节点 parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}// 左右双旋void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);//此节点本身就是插入节点if (bf == 0){parent->_bf = subL->_bf = subLR->_bf = 0;}else if (bf == 1) //右子树的高度较高{parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == -1) //左子树的高度较高{parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}//AVL树的高度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 (root->_bf != rightHeight - leftHeight){cout << root->_data << "节点bf异常" << endl;}return abs(leftHeight - rightHeight) < 2&& _Isbalance(root->_left)&& _Isbalance(root->_right);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}private:Node* _root = nullptr;
};

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

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

相关文章

限制员工上网行为,如何有效管控员工上网行为? 你一定想不到这个方法!

发现员工上班时间刷抖音&#xff1a; 面对这种情况&#xff0c;领导不得火冒三丈&#xff1f;&#xff1f;&#xff1f; 对于员工不恰当的上网行为&#xff0c;非常有可能导致工作效率低下、安全风险增加以及企业形象受损。 因此应该采取一些措施来对员工上网行为进行管理。 …

第三节:在Sashulin中自定义组件

上一节讲解了如何建立一个业务消息流&#xff0c;流程是由组件构成的。目前SMS提供了General、Database、MessageQueue、Socket、WebService、Http、Internet等系列常用组件&#xff0c;如果不满足业务需求&#xff0c;可以进行自定义组件开发。 一、组件开发 1、建立一个Jar…

C及C++每日练习(3)

选择题&#xff1a; 1.以下程序的输出结果是&#xff08;&#xff09; #include <stdio.h> main() { char a[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, *p; int i; i 8; p a i; printf("%s\n", p - 3); } A.6 B. 6789 C. 6 D.789 对于本题&#xff0…

吴恩达机器学习-可选实验室:特征工程和多项式回归(Feature Engineering and Polynomial Regression)

文章目录 目标工具特征工程和多项式回归概述多项式特征选择功能备用视图扩展功能复杂的功能 恭喜! 目标 在本实验中&#xff0c;你将:探索特征工程和多项式回归&#xff0c;它们允许您使用线性回归的机制来拟合非常复杂&#xff0c;甚至非常非线性的函数。 工具 您将利用在以…

2023最新pytorch安装教程,简单易懂,面向初学者(Anaconda+GPU)

一、前言 目前是2023.1.27,鉴于本人安装过程中踩得坑&#xff0c;安装之前我先给即将安装pytorch的各位提个醒&#xff0c;有以下几点需要注意 1.判断自己电脑是否有GPU 注意这点很重要&#xff0c;本教程面向有NVIDA显卡的电脑&#xff0c;如果你的电脑没有GPU或者使用AMD显…

STM32day3

1.思维导图 1.总结任务的调度算法&#xff0c;把实现代码再写一下 /* Definitions for myTask02 */ osThreadId_t myTask02Handle; uint32_t myTask02Buffer[ 64 ]; osStaticThreadDef_t myTask02ControlBlock; const osThreadAttr_t myTask02_attributes {.name "myTa…

代码随想录算法训练营第三十九天|62.不同路径、63. 不同路径 II

62.不同路径 刷题https://leetcode.cn/problems/unique-paths/description/文章讲解https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html视频讲解https://www.bilibili.com/video/BV1ve4y1x7Eu/?vd_sourceaf4853e80f89e28094a5fe1e220d9062 题解&…

react的diff源码

react 的 render 阶段&#xff0c;其中 begin 时会调用 reconcileChildren 函数&#xff0c; reconcileChildren 中做的事情就是 react 知名的 diff 过程 diff 算法介绍 react 的每次更新&#xff0c;都会将新的 ReactElement 内容与旧的 fiber 树作对比&#xff0c;比较出它们…

md5绕过

文章目录 \\和\\\md5数组绕过科学计数法绕过双md加密md5碰撞Hash长度攻击 下面会以同一道题给大家演示&#xff1a; (题目来源与nssctf) 和 在php代码中我们会看到和&#xff0c;虽然两个都是表示相等&#xff0c;但是在细节上会有所部区别 &#xff1a;是弱比较&#xff0c;只…

0201安装报错-hbase-大数据学习

1 基础环境简介 linux系统&#xff1a;centos&#xff0c;前置安装&#xff1a;jdk、hadoop、zookeeper&#xff0c;版本如下 软件版本描述centos7linux系统发行版jdk1.8java开发工具集hadoop2.10.0大数据生态基础组件zookeeper3.5.7分布式应用程序协调服务hbase2.4.11分布式…

Sora 作者被曝读博期间仅发表两篇论文,我们是否需要重塑科研价值观?

众所周知&#xff0c;在当今学术界&#xff0c;论文数量和产出速度常常被视为研究者生产力和学术成就的重要标尺。笔者也面试过很多博士生候选人&#xff0c;大家普遍会以自己读博期间发表过10几篇甚至几十篇论文而骄傲&#xff0c;很少有候选人会强调自己读博期间虽然发表论文…

Matlab|基于目标级联法的微网群多主体分布式优化调度

目录 主要内容 1.1 上层微网群模型 1.2 下层微网模型 部分程序 实现效果 下载链接 主要内容 本文复现《基于目标级联法的微网群多主体分布式优化调度》文献的目标级联部分&#xff0c; 建立微网群系统的两级递阶优化调度模型: 上层是微网群能量调度中心优化调度…

java中的字符串比较(题目作示例)

错误的代码 import java.util.Scanner; public class one {public static void main(String[] args) {Scanner scnew Scanner(System.in);String b"47568";int i0;for ( i 0; i <3; i){String asc.next();if(ab){System.out.println("密码正确&#xff0c;登…

在外包公司干了4年,技术退步2年...

先说情况&#xff0c;大专毕业&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近6年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

白话模电:1.绪论与半导体(考研面试常问问题)

一、什么是信号&#xff1f;什么是电信号&#xff1f; 信号反映消息的物理量&#xff0c;电信号是反应电压或电流变化的物理量。 二、什么是模拟信号&#xff1f;什么是数字信号&#xff1f; 模拟信号是时间和数值上均连续的信号&#xff0c;数字信号是时间和数值上均离散的信号…

[HackMyVM]靶场 Zeug

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

Seata 2.x 系列【5】直接部署

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 概述2. 环境要…

学习嵌入式C语言要掌握到什么程度?

学习嵌入式C语言要掌握到什么程度&#xff1f; 在开始前我分享下我的经历&#xff0c;我刚入行时遇到一个好公司和师父&#xff0c;给了我机会&#xff0c;一年时间从3k薪资涨到18k的&#xff0c; 我师父给了一些 电气工程师学习方法和资料&#xff0c;让我不断提升自己&#…

Java注解介绍

Java注解 注解介绍元注解RetentionTargetDocumentedInherited接口类测试结果 注解介绍 Java注解&#xff08;Annotation&#xff09;是一种元数据&#xff08;Metadata&#xff09;的形式&#xff0c;它可以被添加到Java代码中的类、方法、变量、参数等元素上&#xff0c;以提…

一分钟带你入门Selenium入门!【建议收藏】

Selenium入门 欢迎阅读Selenium入门讲义&#xff0c;本讲义将会重点介绍Selenium的入门知识以及Selenium的前置知识。 自动化测试的基础 在Selenium的课程以前&#xff0c;我们先回顾一下软件测试的基本原理&#xff0c;为我们进一步完成Selenium自动化测试做好准备。 软件…