C++ AVL树

文章目录

  • AVL树的概念
  • AVL树基本框架
  • AVL树的插入
  • AVL树的插入(无旋转)
  • AVL树的插入(旋转操作)
    • 单旋
    • 双旋
    • 旋转代码

上面我们知道二叉搜索树在特殊情况下查找的时间复杂度为O(N),
所以为了解决二叉搜索树不稳定的问题,我们引入了AVL树,也称为平衡二叉树。

二叉搜索树相关知识点

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右
子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述
    我们如果发现平衡因子大于等于2的话,我们就会通过旋转操作使整颗树的平衡因子回到-1,0,1

AVL树基本框架

AVL树的节点结构体:

template<class K, class V>
struct AVLTreeNode
{
     //用三叉链,方便更新祖先
    AVLTreeNode<K, V>* _left;
    AVLTreeNode<K, V>* _right;
    AVLTreeNode<K, V>* _parent;
    pair<K, V> _kv; //存储的数据

    int _bf; // balance factor

    AVLTreeNode(const pair<K, V>& kv)
            :_left(nullptr)
            , _right(nullptr)
            , _parent(nullptr)
            , _kv(kv)
            , _bf(0) 
    {}
};

AVL树的基本结构

template<class K, class V>
struct AVLTree
{
	typedef AVLTreeNode<K, V> Node;
private:
	Node* _root;//定义一个根节点
};

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。AVL树的插入过程可以分为两步:

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

如何更新平衡因子
新增在右,父亲的bf加一
新增在左,父亲的bf减一

  1. 更新完成后,父亲的bf==1/-1,说明父亲插入前的bf一定是0,插入后一边高一边低,需要继续向上更新
  2. 更新完成后,父亲的bf==0,说明父亲在插入前的bf是1/-1,插入后两边高度一致,则不需要继续往上更新了
  3. 更新完成后,父亲的bf==2/-2,打破了平衡,父亲所在的子树要旋转处理
    在这里插入图片描述

AVL树的插入(无旋转)

// 一:按照二叉搜索树的方式插入值;
// 二:调整平衡因子后旋转

bool Insert(const pair<K, V> &kv) 
{
    if (_root == nullptr) // 插入第一个节点
    {
        _root = new Node(kv);
        return true;
    }

    Node *cur = _root;
    Node *parent = nullptr;
    
    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->_right)
            parent->_bf++;
        else
            parent->_bf--;
        if (parent->_bf == 0) // 不用更新
            break;
        else if (parent->_bf == 1 || parent->_bf == -1) // 高度出现变化,往上更新
        {
            parent = parent->_parent;
            cur = cur->_parent;
        }
        else if (abs(parent->_bf) == 2) // parent所在的子树不平衡了,旋转处理
        {
            // 后面旋转
        }
    }
}

AVL树的插入(旋转操作)

单旋

基础旋转

左单旋
在这里插入图片描述

在这里插入图片描述

右单旋
在这里插入图片描述
在这里插入图片描述


双旋

先左旋再右旋在这里插入图片描述
在这里插入图片描述

先右旋再左旋
在这里插入图片描述

在这里插入图片描述

旋转代码

void RotateL(Node *parent) // 左旋
{
    Node *subR = parent->_right;
    Node *subRL = subR->_left;

    parent->_right = subRL;
    subR->_left = parent;

    Node *parentParent = parent->_parent;

    parent->_parent = subR;
    if (subRL)
        subRL->_parent = parent;

    if (_root == parent)
    {
        _root = subR;
        subR->_parent = nullptr;
    }
    else
    {
        if (parentParent->_left == parent)
        {
            parentParent->_left = subR;
        }
        else
        {
            parentParent->_right = subR;
        }

        subR->_parent = parentParent;
    }

    parent->_bf = subR->_bf = 0;
}

void RotateR(Node *parent) // 右旋
{
    Node *subL = parent->_left;
    Node *subLR = subL->_right;

    parent->_left = subLR;
    if (subLR)
        subLR->_parent = parent;

    Node *parentParent = parent->_parent;

    subL->_right = parent;
    parent->_parent = subL;

    if (_root == parent)
    {
        _root = subL;
        subL->_parent = nullptr;
    }
    else
    {
        if (parentParent->_left == parent)
        {
            parentParent->_left = subL;
        }
        else
        {
            parentParent->_right = subL;
        }

        subL->_parent = parentParent;
    }

    subL->_bf = parent->_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;
        subRL->_bf = 0;
        subR->_bf = 1;
    }
    else if (bf == 1)
    {
        // subRL的右子树新增
        parent->_bf = -1;
        subRL->_bf = 0;
        subR->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

void RotateLR(Node *parent) // 先做再右 复用
{
    RotateL(parent->_left);
    RotateR(parent);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于OSPF报文学习

目录 一.OSPF学习补充 &#xff08;1&#xff09;OSPF报文头部 &#xff08;2&#xff09;ospf建立邻居关系 1.Hello报文——建立邻居关系 2.hello报文头部 &#xff08;3&#xff09;OSPF建立邻接关系 1.发送DD报文 2.DD报文头部 &#xff08;4&#xff09;关于DR,BD…

深入OceanBase内部机制:分区机制构建高可用、高性能的分布式数据库基石

码到三十五 &#xff1a; 个人主页 在数据库技术的发展历程中&#xff0c;随着数据量的不断增长和业务需求的日益复杂&#xff0c;如何高效地存储、查询和处理数据成为了关键挑战。OceanBase作为一款高性能、高可用的分布式关系数据库&#xff0c;通过其独特的分区机制&#xf…

03 spring-boot+mybatis+jsp 的增删改查的入门级项目

前言 主要是来自于 朋友的需求 项目概况 就是一个 用户信息的增删改查然后 具体到业务这边 使用 mybatis xml 来配置的增删改查 后端这边 springboot mybatis mysql fastjson 的一个基础的增删改查的学习项目, 简单容易上手 前端这边 jsp 的 基础的试题的增删改查 学习项…

Shell脚本学习记录

0.理解Linux文件权限 0.1 Linux安全性 用户的权限是通过创建用户时分配的用户ID(UID)来追踪的&#xff0c;UID是个数值&#xff0c;每个用户都有一个唯一的UID 0.1.1 /etc/passwd文件 Linux系统使用一个专门的文件/etc/passwd来匹配登录名与对应的UID值&#xff0c;该文件包…

本地体验最强开源模型Llama3+Qnw(支持Windows和Mac)

一键运行大模型本地软件&#xff08;含模型&#xff09;&#xff1a;点击下载 Meta放出Llama3模型了&#xff0c;也应该是这段时间里的一个科技大新闻了。 Llama一直都是开源大语言模型的领头羊驼。 而Llama3又是所有羊驼中最新的领头羊。 可以简单地来看一下官方的对比数据…

Open-Sora:开源版的Sora

项目简介 本项目希望通过开源社区的力量复现Sora&#xff0c;由北大-兔展AIGC联合实验室共同发起&#xff0c;当前我们资源有限仅搭建了基础架构&#xff0c;无法进行完整训练&#xff0c;希望通过开源社区逐步增加模块并筹集资源进行训练&#xff0c;当前版本离目标差距巨大&…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下&#xff1a; #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…

C语言:插入排序

插入排序 1.解释2.步骤3.举例分析示例结果分析 1.解释 插入排序是一种简单直观的排序算法&#xff0c;它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。插入排序在实现上&#xff0c;通常采…

Qt中的 tableView 设置 二进制 十六进制 序号表头

二 进制序号 因为QTableView的垂直表头并不支持使用委托来自定义。 相反&#xff0c;可以通过将自定义的QWidget作为QHeaderView的标签来实现这一目标。 代码&#xff1a; #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include …

使用LSTM模型对心跳时间序列数据预测(Python代码,ipynb环境)

所用模块版本&#xff1a; matplotlib3.7.1 numpy1.24.4 pandas1.5.3 scikit_learn1.2.2 scipy1.10.1 seaborn0.12.2 statsmodels0.14.0 torch1.13.1 torch2.0.1 wfdb4.1.2 主代码&#xff1a; import itertools import pandas as pd import matplotlib.pyplot as plt #完整…

elasticsearch 常用语法汇总

文章目录 前言elasticsearch 常用语法汇总1. 创建索引2. 检索索引信息3. 删除索引4. 文档操作4.1. 对blog_new索引指定文档ID新增4.2. 对blog_new索引不指定文档ID新增&#xff0c;随机文档ID:4.3. 获取文档4.4. 更新文档4.5. 删除文档 5. 查询5.1. 匹配查询5.2. 范围查询5.3. …

计算机视觉的应用29-卷积神经网络(CNN)中的变种:分组卷积、转置卷积、空洞卷积的计算过程

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用29-卷积神经网络(CNN)中的变种&#xff1a;分组卷积、转置卷积、空洞卷积的计算过程。分组卷积将输入通道分为几组&#xff0c;对每组独立进行卷积操作&#xff0c;以减少计算量和模型参数。转置卷…

vue如何发送请求给后端(包括前后端跨域)

目录 有哪些方法可以发送请求要请求先解决跨域问题代理服务器后端解决跨域问题 axios发送请求vue-resource发送请求 有哪些方法可以发送请求 以前可能了解过&#xff1a; xhr 即&#xff1a;new XMLHttpRequest()jQuery 即&#xff1a;$.get $.postaxios fetch 在vue中特有的…

Leetcode 剑指 Offer II 075.数组的相对排序

题目难度: 简单 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定两个数组&#xff0c;arr1 和 arr2&#xff0c; arr2 中的元…

Java NIO

1. IO分类概述 1.1 阻塞与非阻塞 阻塞&#xff08;Blocking&#xff09;和非阻塞&#xff08;Nonblocking&#xff09;是在计算机编程中用于描述I/O操作的两个重要概念。阻塞与非阻塞描述的是线程在访问某个资源时&#xff0c;在该资源没有准备就绪期间的处理方式。 1、阻塞&a…

Android使用AlertDialog实现弹出菜单

最近又开始捣鼓APP&#xff0c;许多api , class都忘记怎么用了&#xff0c;楼下使用AlertDialog实现个弹出菜单&#xff0c;结果直接crash&#xff0c;查了半天&#xff0c;终于即将&#xff0c;记录一下…… 1 实现代码 AlertDialog.Builder mBuilder new AlertDialog.Builde…

后端工程师——C++工程师如何准备面试?

相比 Java 语言方向,C++ 入门简单,精通难,找工作竞争压力更小,但 C++ 依然是近年来招聘的热门岗位之一。本文将从以下三个方面进行详细讲解,帮助你对 C++ 相关岗位的就业前景、岗位要求、学习路线等有更充分的了解。 C++工程师面试准备 上两篇文章对 C++ 工程师的招聘需求…

SpringCloud系列(17)--将服务消费者Consumer注册进Zookeeper

前言&#xff1a;在上一章节中我们把服务提供者Provider注册进了Zookeeper&#xff0c;而本章节则是关于如何将服务消费者Consumer注册进Zookeeper 1、再次创建一个服务提供者模块&#xff0c;命名为consumerzk-order80 (1)在父工程下新建模块 (2)选择模块的项目类型为Maven并…

HPE Aruba Networking推出新一代Wi-Fi 7接入点 助力企业高效应对安全、AI与物联网挑战

HPE ArubaNetworking推出的全新Wi-Fi 7接入点&#xff0c;提供全面的AI就绪边缘IT解决方案&#xff0c;旨在为用户和物联网设备提供安全、高性能的连接服务&#xff0c;以实现数据的捕获和路由&#xff0c;从而满足AI训练和推理需求 休斯顿-2024年4月23日-慧与科技(NYSE: HPE)近…

【golang学习之旅】深入理解字符串string数据类型

系列文章 【golang学习之旅】报错&#xff1a;a declared but not used 【golang学习之旅】Go 的基本数据类型 目录 系列文章使用示例string的底层数据结构关于字符串复制字符串是不可变的如何高效的进行字符串拼接&#xff1f; 使用示例 Go 语言中的字符串只是一个只读的字节…