数据结构与算法——18.avl树

news2024/11/29 4:27:20

这篇文章我们来看一下avl树

目录

1.概述

2.AVL树的实现

1.概述

我们前面讲了二叉搜索树,它是有一个key值,然后比父节点key值大的在左边,小的在右边。这样设计是为了便于查找。但是有一种极端的情况,就是所有的结点都在一边,那查找的时间复杂度和在链表的查找时间复杂度就一样了。那有没有解决方法呢?有!

为了解决上述的问题,人们提出了一种新的概念:平衡二叉树

平衡二叉树:它且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,avl树是平衡二叉树的一种。

这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。

如上图所示,就是一个典型的平衡二叉树。

当平衡二叉树添加或删除节点失去平衡的时候,它就进行自选,从而使自己达到平衡。

下面说一下旋转:

左旋:当根节点的右子树的高度减去左子树的高度大于1时,此时二叉树(肯定是二叉搜索树)不平衡了,需要左旋转。具体做法是:以当前根节点的右孩子为新的根节点,当前跟结点及其左子树为新根节点的左孩子,如果新的根节点原本就有左孩子,则其左孩子作为新根节点的新左孩子的右孩子。

右旋:当根节点的左子树的高度减去右子树的高度大于1时,此时二叉树(肯定是二叉搜索树)不平衡了,需要左旋转。具体做法是:以当前根节点的左孩子为新的根节点,当前跟结点及其右子树为新根节点的右孩子,如果新的根节点原本就有右孩子,则其右孩子作为新根节点的新右孩子的左孩子。

2.AVL树的实现

下面看一下AVL树的实现:

package Tree;
/**AVL树的操作*/
public class L3_AVLTree {

    static class AVLNode{
        int key;
        Object value;
        AVLNode left;
        AVLNode right;
        int height = 1; //高度

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    /**求节点的高度*/
    private int getHeight(AVLNode node){
        return node == null? null:node.height;
    }

    /**更新节点的高度*/
    private void updateHeight(AVLNode node){
        node.height =
                Integer.max(getHeight(node.left),getHeight(node.right))+1;
    }

    /**求一个节点左右子树的高度差*/
    private int bf(AVLNode node){
        return (getHeight(node.left) - getHeight(node.right));
    }

    /**
     *右旋
     * 参数:失衡的结点(即要选择的结点)
     * 返回值:新的根节点
     * */
    private AVLNode rightRotate(AVLNode node){
        AVLNode nodeLeft = node.left;
        AVLNode nodeRightLeft = nodeLeft.right;
        nodeLeft.right = node;//上位
        node.left = nodeRightLeft;//换爹
        updateHeight(node);
        updateHeight(nodeLeft);
        return nodeLeft;
    }

    /**
     * 左旋
     * 参数:失衡的结点(即要选择的结点)
     * 返回值:新的根节点
     * */
    private AVLNode leftRotate(AVLNode node){
        AVLNode nodeRight = node.right;
        AVLNode nodeRightLeft = nodeRight.left;
        nodeRight.left = node;//上位
        node.right = nodeRightLeft;//换爹
        updateHeight(node);
        updateHeight(nodeRight);
        return nodeRight;
    }

    /**
     * 先左旋左子树,再右旋根节点
     * */
    private AVLNode leftRightRotate(AVLNode node){
        node.left = leftRotate(node.left);
        return rightRotate(node);
    }

    /**
     * 先右旋右子树,再左旋根节点
     * */
    private AVLNode rightLeftRotate(AVLNode node){
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

    /**检查结点是否失衡,如果失衡,则重新平衡结点*/
    private AVLNode balance(AVLNode node){
        if (node == null){
            return null;
        }
        int bf = bf(node);
        if (bf > 1 && bf(node.left) >= 0){//LL
            return rightRotate(node);
        }else if (bf > 1 && bf(node.left) < 0){//LR
            return leftRightRotate(node);
        }else if (bf < -1 && bf(node.right) <= 0){//RR
            return leftRotate(node);
        }else if (bf < -1 && bf(node.right) > 0){//RL
            return rightLeftRotate(node);
        }
        return node;
    }

    AVLNode root;
    /**新增结点*/
    public void put(int key,Object value){
        root = doPut(root,key,value);
    }
    private AVLNode doPut(AVLNode node,int key,Object value){
        //找到空位,创建新节点
        if (node == null){
            return new AVLNode(key,value);
        }
        //key已有,更新操作
        if (key == node.key){
            node.value = value;
            return node;
        }
        //继续查找
        if (key < node.key){
            node.left = doPut(node.left,key,value);//向左
        }else {
            node.right = doPut(node.right,key,value);//向右
        }
        updateHeight(node);
        return balance(node);
    }

    public void remove(int key){
        root = doRemove(root,key);
    }
    private AVLNode doRemove(AVLNode node,int key){
        if (node == null){
            return null;
        }
        //没找到key
        if (key < node.key){
            node.left = doRemove(node.left,key);
        }else if (node.key < key){
            node.right = doRemove(node.right,key);
        }else { //找到key:没有孩子;只有一个孩子,两个孩子都有
            if (node.left == null && node.right == null){
                return null;
            }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(node.right,s.key);
                s.left = node.left;
                node = s;
            }
        }

        //更新 高度
        updateHeight(node);
        //检查失衡
        return balance(node);
    }
}

总结:会者不难,难者不会。知道定义,会画图,会递归,那就能写出来。然后再查缺补漏一下,就没啥问题。

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

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

相关文章

7.3 调用函数

前言&#xff1a; 思维导图&#xff1a; 7.3.1 函数调用的形式 我的笔记&#xff1a; 函数调用的形式 在C语言中&#xff0c;调用函数是一种常见的操作&#xff0c;主要有以下几种调用方式&#xff1a; 1. 函数调用语句 此时&#xff0c;函数调用独立存在&#xff0c;作为…

艺术表现形式

abstract expressionism 抽象表现主义 20世纪中期的一种艺术运动&#xff0c;包括多种风格和技巧&#xff0c;特别强调艺术家通过非传统和通常非具象的手段表达态度和情感的自由。 抽象表现主义用有力的笔触和滴落的颜料来表达情感和自发性。 简单地结合“abstract expression…

基于Java的服装销售平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

paddle2.3-基于联邦学习实现FedAVg算法-CNN

目录 1. 联邦学习介绍 2. 实验流程 3. 数据加载 4. 模型构建 5. 数据采样函数 6. 模型训练 1. 联邦学习介绍 联邦学习是一种分布式机器学习方法&#xff0c;中心节点为server&#xff08;服务器&#xff09;&#xff0c;各分支节点为本地的client&#xff08;设备&#…

【k8s】集群搭建篇

文章目录 搭建kubernetes集群kubeadm初始化操作安装软件(master、所有node节点)Kubernetes Master初始化Kubernetes Node加入集群部署 CNI 网络插件测试 kubernetes 集群停止服务并删除原来的配置 二进制搭建(单master集群)初始化操作部署etcd集群安装Docker部署master节点解压…

SpringBoot 如何使用 Spring Data MongoDB 访问 MongoDB

使用 Spring Boot 和 Spring Data MongoDB 访问 MongoDB 数据库 在现代应用程序开发中&#xff0c;许多应用都依赖于数据库来存储和检索数据。MongoDB 是一个流行的 NoSQL 数据库&#xff0c;而 Spring Boot 是一个广泛使用的 Java 开发框架。本文将介绍如何使用 Spring Boot …

28383-2012 卷筒料凹版印刷机 学习笔记

声明 本文是学习GB-T 28383-2012 卷筒料凹版印刷机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了卷筒料凹版印刷机的型式、基本参数、要求、试验方法、检验规则、标志、包装、运输与 贮存。 本标准适用于机组式的卷筒料凹版…

网络协议--链路层

2.1 引言 从图1-4中可以看出&#xff0c;在TCP/IP协议族中&#xff0c;链路层主要有三个目的&#xff1a; &#xff08;1&#xff09;为IP模块发送和接收IP数据报&#xff1b; &#xff08;2&#xff09;为ARP模块发送ARP请求和接收ARP应答&#xff1b; &#xff08;3&#xf…

28390-2012 幕墙铝型材高速五面加工中心

声明 本文是学习GB-T 28390-2012 幕墙铝型材高速五面加工中心. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了幕墙铝型材高速五面加工中心的分类、技术要求、试验方法、检测规则、标牌、使用说 明书、包装、运输和贮存。 本标…

基于Java的游戏检索系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户功能已注册用户的功能后台功能管理员功能具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博…

oracle GBK未定义编码使用Unicode写入特殊字符e000迁移lightdb-x测试

E:\HS\LightDBSVN\23.3sql文件\迁移工具\caofa\config\application.properties gbk-->uft8: logging.configclasspath:log4j2.xml # ???? etl.global.sourceDatabaseoracle etl.global.targetDatabaselightdb etl.global.showSqlfalse etl.global.fastFailfalse etl.g…

Python操作自动化

迷途小书童 读完需要 3分钟 速读仅需 1 分钟 当我们需要自动化进行一些重复性的任务时&#xff0c;Python 中的 pyautogui 库就可以派上用场了&#xff0c;这个库可以模拟鼠标和键盘的操作&#xff0c;让我们的程序可以像人一样与计算机进行交互。 首先&#xff0c;我们需要安装…

Beats Studio Buds 连接 Windows 11 声音输出不显示设备

Beats Studio Buds 连接 Windows 11 声音输出不显示设备 Beats Studio Buds 蓝牙耳机连接Windows 11电脑后&#xff0c;无法通过耳机播放声音&#xff0c;在声音输出选项中也没有耳机选项。 问题 蓝牙耳机连接电脑。 在声音输出中查看输出设备选项。 解决方法 以管理员身…

LeetCode每日一题 | 309.买卖股票的最佳时机含冷冻期

题目链接&#xff1a; 309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 算法图解&#xff1a; 解题代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();vector&…

求∑(1,n)⌊k/i⌋∗i

对于[k/i]*i,我们可以分两端&#xff0c;前,最多有段&#xff0c;后边从到n&#xff0c;取值范围为1-&#xff0c;所以最多有段&#xff0c;共2*段。对于每段从i开始&#xff0c;其上界jk/(k/i)&#xff08;维持k/i不变最大范围i-j&#xff09;。 计算[k/i]*i时间复杂度降到级…

Android 命令行工具简介

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、相关工具3.1 Android SDK 命令行工…

TempleteMethod

TempleteMethod 动机 在软件构建过程中&#xff0c;对于某一项任务&#xff0c;它常常有稳定的整体操作结构&#xff0c;但各个子步骤却有很多改变的需求&#xff0c;或者由于固有的原因 &#xff08;比如框架与应用之间的关系&#xff09;而无法和任务的整体结构同时实现。如…

数据结构学习笔记(基础)

绪论 数据结构三要素&#xff08;数据的基本单位是数据元素&#xff0c;数据元素可由若干个数据项组成&#xff0c;一个数据项是构成数据元素的不可分割的最小单位&#xff09; 数据&#xff1a;指的是能被计算机识别、存储和加工处理的信息载体&#xff08;如 Word 文档&#…

【设计模式_实验①_第六题】设计模式——接口的实验模拟应用实验作业一

【实验要求】 货车要装载一批货物&#xff0c;货物由三种商品组成&#xff1a;电视、计算机和洗衣机。卡车需要计算出整批货物的重量。 【实验步骤】UML 过程 在这里插入代码片 public interface ComputerWeight {public abstract double computerWeight(); }public class T…

GD32 看门狗

1. 看门狗的概念 2. 独立看门狗 独立看门狗的原理&#xff1a;设定一个重载值。赋值计数器。每来一个脉冲计数值减减。如果计数值减到0。还没有去喂狗就会产生复位。所以在计数值在0~重载值范围必须要喂一次狗。 在键值寄存器(IWDG_KR)中写入0xCCCC&#xff0c;开始启用独立看…