数据结构 -- AVL树

news2024/9/30 13:27:10

1、定义

平衡搜索二叉树,相对于搜索二叉树而言,AVL树又多了一个性质:左右子树的高度差不大于1.

2、平衡因子,balance factor,以下简称bf,是左子树高度减去右子树的高度

  1. bf > 1,左边子树高
  2. bf < -1,右边子树高
  3. bf = 0, -1, 1 是平衡的
    // 节点的高度
    private int height(AVLNode node) {
        return node == null ? 0 : node.height;
    }

    // 更新一个节点的高度,用于新增,删除,旋转
    private void updateHeight(AVLNode node) {
        node.height = Math.max(height(node.right), height(node.left)) + 1;
    }

3、失衡的四种情况:

  • LL :失衡节点左子树高,并且失衡节点的左孩子的左子树高----- 一次右旋
  • LR:失衡节点左子树高,并且失衡节点的左孩子的右子树高  ---- 左子树先左旋,失衡节点右旋
  • RL:失衡节点的右子树高,并且失衡节点的右孩子的左子树高 ---- 右子树先右旋,失衡节点在左旋
  • RR:失衡节点的右子树高,并且失衡节点的右孩子的右子树高 ---- 一次左旋

4、旋转操作

右旋方法:传入要旋转的节点,返回值是新的根节点

    public AVLNode rightRotate(AVLNode red) {
        AVLNode yellow = red.left;
        AVLNode green = yellow.right;

        yellow.right = red;
        red.left = green;

        // 更新节点高度,其实只要更新红色和黄色节点高度就可以了
        updateHeight(red);
        updateHeight(yellow);
        return yellow;
    }

左旋方法

 

    public AVLNode leftRotate(AVLNode red) {
        AVLNode yellow = red.right;
        AVLNode green = yellow.left;

        yellow.left = red;
        red.right = green;
        
        // 更新节点高度,其实只要更新红色和黄色节点高度就可以了
        updateHeight(red);
        updateHeight(yellow);
        return yellow;
    }

左右旋方法 --- 先左旋左子树,在右旋跟节点

  1. 先对做子树左旋,左旋完之后返回的节点,作为该节点的左子树
  2. 然后对这个树进行右旋
    public AVLNode leftRightRotate(AVLNode node) {
        node.left = leftRotate(node.left);
        return rightRotate(node);
    }

 

 右左旋方法:

  1. 先对右子树右旋,右旋完之后的节点作为该节点的右子树
  2. 然后对该节点进行左旋
    public AVLNode rightLeftRotate(AVLNode node) {
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

 

 

5、平衡操作,发生在新增,删除时,对树进行平衡的操作

检查节点是否失衡,如果失衡,就平衡,然后返回新的跟节点

    // 检查节点是否失衡,如果失衡,就平衡,然后返回新的跟节点
    public AVLNode balance(AVLNode node) {
        if (node == null) {
            return null;
        }

        int bf = bf(node);
        if (bf == 0 || bf == 1 || bf == -1) {
            return node;
        }

        if (bf > 1) {
            int leftBf = bf(node.left);
            if (leftBf >= 0) { // --注意等号,发生在删除时
                // LL
                return rightRotate(node);
            } else {
                // LR
                return leftRightRotate(node);
            }
        } else {
            int rightBf = bf(node.right);
            if (rightBf <= 0) { // --注意等号,发生在删除时
                // RR
                return leftRotate(node);
            } else {
                // RL
                return rightLeftRotate(node);
            }
        }
    }

6、节点的插入操作

 插入的时候,要更新节点高度,并平衡树

    public AVLNode root;

    // 插入一个节点
    public void put(int key, Object value) {
        root = doPut(root, key, value);
    }

    // 递归调用
    public AVLNode doPut(AVLNode node, int key, Object value) {
        // 节点空,就返回一个新节点,
        if (node == null) {
            return new AVLNode(key, value);
        }
        if (node.key == key) {
            node.value = value;
            return node;
        }

        if (node.key > key) {
            node.left = doPut(node.left, key, value); // 向左
        } else {
            node.right = doPut(node.right, key, value); // 向右
        }

        // 更新节点的高度,并对树进行平衡操作
        updateHeight(node);
        return balance(node);
    }

7、 节点的删除操作

    public void remove(int key) {
        root = doRemove(root, key);
    }

    public AVLNode doRemove(AVLNode node, int key) {
        if (node == null) {
            return null;
        }

        if (key < node.key) {
            node.left = doRemove(node.left, key);
        } else if (node.key < key) {
            node.right = doRemove(node.right, key);
        } else {
            // 找到了要删除的节点
            if (node.left == null) {
                node = node.right;
            } else if (node.right == null) {
                node = node.left;
            } else {
                // 找后继节点
                AVLNode s = node.right;
                while (s.left != null) {
                    s = s.left;
                }
                s.right = doRemove(s.right, s.key);
                s.left = node.left;
                node = s;
            }
        }
        updateHeight(node);
        return balance(node);
    }

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

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

相关文章

Java最新版发送阿里短信教程

一、概述&#xff1a; 为什么现在的企业越来越多使用阿里云短信服务&#xff0c;究其原因是阿里云短信服务是一种可靠、高效、安全的短信发送服务&#xff0c;它具有以下优点&#xff1a; 高可靠性&#xff1a;阿里云短信服务采用全球领先的短信网关进行短信发送&#xff0c;确…

自定义线程池

自定义线程池原理 线程池中分为核心线程和临时线程&#xff1b;首先创建核心线程使用&#xff0c;创建之后一直存在线程池&#xff0c;核心线程被占用并且队列任务已满&#xff0c;才会创建临时线程&#xff1b;临时线程使用超过自定义临时线程最大数时会触发自定义的任务拒绝策…

你猜,一个TCP连接能发多少HTTP请求?

又见面了&#xff0c;我的网工朋友 曾经有这么一道经典面试题&#xff1a;从 URL 在浏览器被被输入到页面展现的过程中发生了什么&#xff1f; 相信大多数准备过的同学都能回答出来&#xff0c;但是如果继续问&#xff1a; 收到的 HTML 如果包含几十个图片标签&#xff0c;这…

【Vue】学习笔记-slot插槽

slot插槽 <slot>插槽&#xff1a;让父组件可以向子组件指定位置插入html结构&#xff0c;也是一种组件间通信的方式&#xff0c;适用于父组件>子组件 分类&#xff1a;默认插槽、具名插槽、作用域插槽 使用方式 a.默认插槽 b.具名插槽 父组件指明放入子组件的哪个插…

Django从Models 10分钟定制一个Admin后台

简介 Django自带一个Admin后台, 支持用户创建,权限配置和所有模型的增删改查功能, 只需要一些简单的配置就可快速得到一个开箱可用的后台管理系统 操作步骤 1. 更改设置,使用中文/亚洲时区 修改项目下django_shop目录下的settings.py文件 修改以下三行 LANGUAGE_CODE zh-h…

深度学习AI编译器-LLVM简介

1、什么是LLVM LLVM是一个编译器框架。LLVM作为编译器框架&#xff0c;是需要各种功能模块支撑起来的&#xff0c;你可以将clang和lld都看做是LLVM的组成部分&#xff0c;框架的意思是&#xff0c;你可以基于LLVM提供的功能开发自己的模块&#xff0c;并集成在LLVM系统上&…

干货 | 出国留学申请必备的6种材料,速来!!!

Hello,大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 我们又见面啦~你还好吗&#xff1f; 这是喵君姐姐的第n篇诚意推送~ 01 为什么要留学&#xff1f; 想去看外面的世界&#xff1f;想要打破科研的壁垒&#xff1f;想去更好的平台提升自己&#xff1f…

Android进阶之路 - 存、取、读 本地 Json 文件

最近在开发中又开始加载一些本地的json数据源&#xff0c;回头看之前竟然没记录&#xff0c;赶紧记录一波 ~ 如何准备一个合格的json文件?AndoridStudio中如何存放json文件&#xff1f;如何读取本地Json文件数据源?Java 版本Kotlin 版本 如何准备一个合格的json文件? 准备一…

GPT模型结合Python-GEE遥感云大数据分析、管理与可视化技术

查看原文>>>GPT模型支持下的Python-GEE遥感云大数据分析、管理与可视化技术及多领域案例应用 目录 第一章、理论基础 第二章、开发环境搭建 第三章、遥感大数据处理基础与ChatGPT等AI模型交互 第四章、典型案例操作实践 第五章、输入输出及数据资产高效管理 第…

dvwa靶场通关(三)

第三关&#xff1a;CSRF&#xff08;跨站请求伪造&#xff09; csrf跨站请求伪造&#xff1a;是一种对网站的恶意利用。尽管听起来像跨站脚本&#xff0c;但它与xss非常不同&#xff0c;xss利用站点内受信任用户&#xff0c;而csrf则通过伪造来自受信任用户的请求来利用受信任…

Springboot +spring security,认证方式---Form表单认证的实现(二)

一.简介 这篇文章来学习下security的认证方式其中的Form表单认证 二.Spring Security的认证方式 2.1什么是认证 认证: 就是用来判断系统中是否存在某用户&#xff0c;并判断该用户的身份是否合法的过程&#xff0c;解决的其实是用户登录的问题。认证的存在&#xff0c;是为…

【Java-10】深入浅出线程安全、死锁、状态、通讯、线程池

主要内容 线程安全线程死锁线程的状态线程间通讯线程池 1 线程安全 1.1 线程安全产生的原因 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据错乱就是线程的安全问题了 问题出现的原因 : 多个线程在对共享数据进行读改写的时候&#xff0c;可能导致的数据…

有哪些辅助计算机开发的工具推荐?

以下是一些辅助计算机开发的工具推荐&#xff1a; 集成开发环境&#xff08;Integrated Development Environment&#xff0c;IDE&#xff09;&#xff1a; 常用的IDE包括Visual Studio、Eclipse、IntelliJ IDEA、PyCharm等&#xff0c;它们提供了代码编辑器、调试器、构建工…

TDengine 集成 EMQX 通过规则引擎实现设备数据直接入库

背景 曾使用过 IoTDB 自带的 MQTT Broker 实现了设备数据入库&#xff0c;那么使用 TDengine 时&#xff0c;我们可以借助 EMQX &#xff08;一款优秀的国产开源 MQTT Broker &#xff09;的规则引擎结合 TDengine 的 RESTful API 完成设备数据的路由与入库。 用到的工具 TD…

chatgpt赋能python:Python下载Module的指南

Python下载Module的指南 作为一门高级编程语言&#xff0c;Python凭借其简单易学、高效便捷的特点&#xff0c;越来越受到广大程序员的喜爱。Python社区也逐渐发展壮大&#xff0c;丰富的第三方Module为我们提供了更多功能强大、用途广泛的工具。本篇文章将介绍Python下载Modu…

从汇编代码的角度去理解C++多线程编程问题

目录 1、多线程问题实例 2、理解该多线程问题的预备知识 2.1、二进制机器码和汇编代码 2.2、多线程切换与CPU时间片 2.3、多线程创建与线程函数 3、从汇编代码的角度去理解多线程问题 4、问题解决办法 5、熟悉汇编代码有哪些用处&#xff1f; 5.1、在代码中插入汇编代…

信号处理与分析-傅里叶

目录 一、引言 二、傅里叶级数 1. 傅里叶级数的定义 2. 傅里叶级数的性质 三、傅里叶变换 1. 傅里叶变换的定义 2. 傅里叶变换的性质 四、离散傅里叶变换 1. 离散傅里叶变换的定义 2. 离散傅里叶变换的性质 五、应用实例 1. 信号处理 2. 图像处理 六、总结 一、引…

Revit中窗族的默认窗台高度与底高度是一样?

​  一、窗族的默认窗台高度与底高度是一样的吗? 窗族的系统设定中有一个自带的参数就是默认窗台高度&#xff0c;指的是窗户放置的时候窗户最底端离墙的最底端高度。 当我们创建一个建筑样板将我们创建好的窗族放置好的时候&#xff0c;这个参数就在窗的类型属性中&#xf…

2023年上半年 软件设计师答案解析

前言&#xff1a;2023年上半年软考已经落幕了&#xff0c;学长整理了一下软件设计师的题目以及个人理解的答案&#xff08;仅供参考&#xff09;希望能够帮助参加软考的各个小伙伴能够清晰的估分&#xff0c;希望大家都能通过考试~ 目录 2023年上半年 软件设计师 上午试卷 2023…

C Primer Plus第十二章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 1.不使用全局变量&#xff0c;重写程序…