数据结构 AVL树

news2024/12/28 4:05:32

概述

AVL树的主要特点是在插入或者删除节点后,树会自动保持其平衡性,从而保证了最坏情况下,查找、插入和删除的时间复杂度都是O(log n)。注意AVL树是符合二叉搜索树的规则,即左子树小于根节点数值,右子树大于节点数值

平衡因子

  • 平衡因子:一个节点左子树的高度减去右子树高度的值
    • BF = 左子树高度 - 右子树高度
  • AVL树节点,其平衡因子取值范围只可以在-1\0\1
    • BF = 0:左子树高度等于右子树高度,节点平衡
    • BF = 1:左子树高度比右子树高度多1,平衡
    • BF = -1:右子树高度比左子树高度多1 ,平衡
    • 超出上述范围,就会导致不平衡需要进行旋转

旋转

单右旋

左子树的节点高度比右子树高的时候,则单右旋(左子树左侧节点变高的时候---左左右旋)

【注:圆形数字为平衡因子】

 

 

  • 将失衡节点 y 的左子节点 x 作为新的根节点
  • x 的右子树 T2 作为 y 的左子树(因为旋转后,y 将成为 x 的右子节点)
  • 更新 yx 的高度
  • 返回新的根节点 x
Node* rightRotate(Node* y) {
    Node* x = y->left;
    y->left = x->right;
    x->right = y;
    updateHeight(y);
    updateHeight(x);
    return x;
}

单左旋

右子树的高度比左子树高2时,新节点插入到了较高右子树的右侧(右右插入--左旋

 

  • 将失衡节点 x 的右子节点 y 作为新的根节点。
  • y 的左子树 T2 作为 x 的右子树(因为旋转后,x 将成为 y 的左子节点)。
  • 更新 xy 的高度。
  • 返回新的根节点 y
Node* leftRotate(Node* x) {
    Node* y = x->right;
    x->right = y->left;
    y->left = x;
    updateHeight(x);
    updateHeight(y);
    return y;
}

左右旋

当新节点插入到较高左子树的右侧,则对该树先进左单选然后再右单旋(左右高,左右旋

  • 当某个节点的左子树高度比右子树高2,且左子树的右子树高度比左子树的左子树高的时候,先对左子树进行一次左旋转,然后再对根节点进行一次右旋

先左旋(用一个具体情况分析):平衡因子不再标注

再右旋:90头结点进行右旋 

右左旋

插入的新节点在较高右子树的左侧:先右单旋再左单旋(右左高--右左旋)

 

具体事例分析:先右旋 90

【注:数据设计有问题,将右子树下左子树的60改为61,左旋已更正】

再左旋,60左旋

代码实现

height函数:如果节点存在,返回节点的高度;如果节点为空,则返回0 

int height(Node* node) {
    return node ? node->height : 0;
}

更新结节点高度,节点的高度等于左右子树中较高的高度+1 

void updateHeight(Node* node) {
    node->height = 1 + std::max(height(node->left), height(node->right));
}

 插入逻辑总结

  • 递归插入节点:通过递归方式将新节点插入到适当的位置。如果节点为空,创建并返回一个新节点。
  • 更新高度:插入节点后,递归回溯过程中更新每个节点的高度。
  • 计算平衡因子:检查插入节点后树的平衡状态。
  • 旋转调整:根据平衡因子进行适当的旋转:
    • 左左情况:右旋。
    • 右右情况:左旋。
    • 左右情况:先左旋再右旋。
    • 右左情况:先右旋再左旋。
  • 返回节点:递归返回调整后的树的根节
Node* insert(Node* node, int key) {
    if (!node) return new Node(key);

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node; // 相同的key不插入

    updateHeight(node);

    int balance = getBalance(node);

    // 根据平衡因子调整树
    if (balance > 1 && key < node->left->key)  // 左左情况
        return rightRotate(node);

    if (balance < -1 && key > node->right->key) // 右右情况
        return leftRotate(node);

    if (balance > 1 && key > node->left->key) { // 左右情况
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    if (balance < -1 && key < node->right->key) { // 右左情况
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node; // 返回未变的节点
}

inOrder:升序打印AVL树中所有的节点

  • 递归遍历左子树。
  • 打印当前节点的值。
  • 递归遍历右子树
void inOrder(Node* root) {
    if (root) {
        inOrder(root->left);
        std::cout << root->key << " ";
        inOrder(root->right);
    }
}

 全代码

#include <iostream>

// AVL树节点定义
struct Node {
    int key;          // 节点值
    Node* left;       // 左子树
    Node* right;      // 右子树
    int height;       // 节点高度

    Node(int k) : key(k), left(nullptr), right(nullptr), height(1) {} // 构造函数
};

// 获取节点高度
int height(Node* node) {
    return node ? node->height : 0;
}

// 更新节点高度
void updateHeight(Node* node) {
    node->height = 1 + std::max(height(node->left), height(node->right));
}

// 计算平衡因子
int getBalance(Node* node) {
    return node ? height(node->left) - height(node->right) : 0;
}

// 右旋操作
Node* rightRotate(Node* y) {
    Node* x = y->left;
    y->left = x->right;
    x->right = y;
    updateHeight(y);
    updateHeight(x);
    return x;
}

// 左旋操作
Node* leftRotate(Node* x) {
    Node* y = x->right;
    x->right = y->left;
    y->left = x;
    updateHeight(x);
    updateHeight(y);
    return y;
}

// 插入节点并保持AVL树平衡
Node* insert(Node* node, int key) {
    if (!node) return new Node(key);

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node; // 相同的key不插入

    updateHeight(node);

    int balance = getBalance(node);

    // 根据平衡因子调整树
    if (balance > 1 && key < node->left->key)  // 左左情况
        return rightRotate(node);

    if (balance < -1 && key > node->right->key) // 右右情况
        return leftRotate(node);

    if (balance > 1 && key > node->left->key) { // 左右情况
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    if (balance < -1 && key < node->right->key) { // 右左情况
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node; // 返回未变的节点
}

// 中序遍历
void inOrder(Node* root) {
    if (root) {
        inOrder(root->left);
        std::cout << root->key << " ";
        inOrder(root->right);
    }
}

int main() {
    Node* root = nullptr;

    // 插入节点
    root = insert(root, 10);
    root = insert(root, 20);
    root = insert(root, 30);
    root = insert(root, 40);
    root = insert(root, 50);
    root = insert(root, 25);

    // 打印中序遍历结果
    std::cout << "中序遍历结果:" << std::endl;
    inOrder(root);

    return 0;
}

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

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

相关文章

为什么说视频监控平台必须要有转码能力?视频汇聚平台EasyCVR视频转码能力解析

在当今数字化时代&#xff0c;视频监控技术已成为社会安全、城市管理、企业运营等多个领域不可或缺的一部分。随着监控系统的广泛应用和技术的不断进步&#xff0c;视频数据的处理与传输需求日益复杂多样&#xff0c;这使得视频监控平台必须具备强大的转码能力显得尤为重要。以…

Python模拟退火算法

目录 模拟退火算法简介模拟退火算法的步骤模拟退火算法的Python实现场景&#xff1a;函数优化问题 代码解释总结 模拟退火算法简介 模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种基于物理退火过程的随机搜索算法&#xff0c;用于寻找全局最优解。其灵感…

如何用OceanBase实现HBase架构升级

随着数据量的爆炸性增长&#xff0c;特别是半结构化和非结构化数据的涌现&#xff0c;传统关系型数据库如 MySQL 遭遇了前所未有的挑战。这一背景下&#xff0c;为非结构化数据管理而生的 NoSQL 数据库&#xff0c;以及旨在解决海量数据存储难题的分布式技术应运而生&#xff0…

Day08-K8S安全框架RBAC

Day08-K8S安全框架RBAC 0、昨日内容回顾:1、K8S安全框架1.1 K8S的安全架构流程图解1.2 RBAC1.3 基于用户的权限管理实战1.4 RBAC基于组的方式认证:1.5 serviceaccount 2、持久卷与动态存储2.1 传统基于存储卷的方式挂载的缺点2.2 引入PV和PVC实现后端存储解耦2.3 引入动态存储类…

鸿蒙(API 12 Beta3版)【播控特性简介】分布式媒体会话

使用媒体播控&#xff0c;可以简单高效地将音视频投放到其他HarmonyOS设备上播放&#xff0c;如在手机上播放的音视频&#xff0c;可以投到2in1设备上继续播放。 HarmonyOS提供了统一的应用内音视频投播功能设计&#xff0c;通过使用系统提供的投播组件和接口&#xff0c;应用…

FLUX:AI 图像生成的新王者;VideoDoodles:在视频中添加手绘涂擦动画丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

提升家居品质,从一颗螺丝开始:深度解析定制螺丝服务

在家具设计行业&#xff0c;每一件作品都是设计师动能的结晶&#xff0c;它们不仅背负着对审美的追寻&#xff0c;而且还具有对作用考虑。在这过程中&#xff0c;螺钉作为连接各部件的主要部件&#xff0c;其重要性不言而喻。家具的坚固性和美观度完全取决于螺钉的挑选运用。传…

【机器学习】可视化注意力的基本概念以及几种常见的注意力可视化方法

引言 在机器学习中&#xff0c;尤其是深度学习领域&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;已经成为处理序列数据&#xff08;如自然语言处理、图像识别等&#xff09;的一种重要技术。可视化注意力可以帮助我们理解模型是如何关注输入数据的特定部…

数据结构-常见的七大排序

上节中我们学习了七大排序中的五种(插入排序、希尔排序、堆排序、选择排序、交换排序) 数据结构-常见的七大排序-CSDN博客 这节我们将要学习快速排序(hoare、指针法、挖洞法(快排的延伸)、快速排序非递归(栈)) 1.快速排序 1.1 hoare法 1.1思路 1.选出一个key&#xff0c;一…

Centos 7.9 安装 图解版 小白必看 最新

兄弟们&#xff0c;非小白就散了吧 安装VMware和CentOS7系统 CentOS7.9.io镜像下载 建议 阿里云下载cents镜像ios 链接如下 https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/CentOS-7-x86_64-DVD-2207-02.iso VMware安装 win7用 12 win10 用 15 win11用 16.2.4 …

Layui——隐藏表单项后不再进行验证

目录 修改后的部分代码 修改后的完整代码 我编辑用户信息和添加新用户用的是同一个表单&#xff0c;不同的是编辑用户信息里没有密码项和确认密码项&#xff0c;但是把它们隐藏后仍然要进行验证&#xff0c;也就是说它们俩的验证并没有随着表单项的隐藏而关闭。原因&#xf…

阴阳脚数码管

1.小故事 最近&#xff0c;我接到了一个既“清肺”又“烧脑”的新任务&#xff0c;设计一个低功耗蓝牙肺活量计。在这个项目中我们借鉴了一款蓝牙跳绳的硬件设计方案&#xff0c;特别是它的显示方案——数码管。 在电子工程领域&#xff0c;初学者往往从操作LED开始&#xff…

JavaSE 网络编程

什么是网络编程 计算机与计算机之间通过网络进行数据传输 两种软件架构 网络编程3要素 IP IPv4 IPv6 Testpublic void test01() throws UnknownHostException { // InetAddress.getByName 可以是名字或ipInetAddress address InetAddress.getByName("LAPTOP-7I…

混剪素材哪里下载?网盘格式的素材网站分享

在今天的文章中&#xff0c;我们将深入探讨如何在互联网上寻找高质量的混剪素材&#xff0c;尤其是对于短视频和自媒体制作者来说&#xff0c;寻找合适的素材至关重要。在本文中&#xff0c;我将向大家推荐一系列优秀的素材下载网站&#xff0c;这些网站提供可以直接下载到百度…

Java入门基础16:集合框架1(Collection集合体系、List、Set)

集合体系结构 Collection是单列集合的祖宗&#xff0c;它规定的方法&#xff08;功能&#xff09;是全部单列集合都会继承的。 collection集合体系 Collection的常用方法 package com.itchinajie.d1_collection;import java.util.ArrayList; import java.util.HashSet;/* * 目…

分布式事务和一致性

分布式事务是什么&#xff1f; 分布式事务是指在分布式系统中涉及到多个数据库或多个应用程序之间的事务处理&#xff0c;这些数据库或应用程序可能 布在不同的物理节点上&#xff0c;甚至可能位于不同的地理位置&#xff0c;在分布式事务中&#xff0c;需要确保所有参与者的事…

C++ 之动手写 Reactor 服务器模型(一):网络编程基础复习总结

基础 IP 地址可以在网络环境中唯一标识一台主机。 端口号可以在主机中唯一标识一个进程。 所以在网络环境中唯一标识一个进程可以使用 IP 地址与端口号 Port 。 字节序 TCP/IP协议规定&#xff0c;网络数据流应采用大端字节序。 大端&#xff1a;低地址存高位&#xff0c…

[陇剑杯 2021]wifi WP

9.1小王往upload-labs上传木马后进行了cat /flag&#xff0c;flag内容为_____________。&#xff08;压缩包里有解压密码的提示&#xff0c;需要额外添加花括号&#xff09; 附件信息&#xff1a; 拿到附件 先看服务器.pcapng 可以发现只有发出去的包&#xff0c;且为哥斯…

Golang实现简单的HTTP服务,响应RESTful请求判断形状大小

题目要求&#xff1a; 题目 1.shape 接口有面积Area() float64和 周长Perimeter()fioat64 两个法。为Circle Rectangle实现shape 接口。 2.实现isGreater(shape1&#xff0c;shape2 shape)boo1 函数&#xff0c;用于比较两个形状的大小,并使用单元测试验证 3.实现http.Handle…

反常识心理学——受助者恶意 / 如何防备受助者恶意的发生

原创 大渔 大渔大师课 贯穿电影《消失的她&#xff08;2013年上映&#xff09;》中全片的两个反常识心理学效应&#xff1a;曼德拉效应、受助者恶意。 「被篡改的记忆—曼德拉效应 」 何非与妻子去国外旅行&#xff0c;妻子却离奇失踪&#xff0c;正在何非焦急寻找之时&…