【数据结构进阶】二叉平衡树

news2024/9/28 15:21:00

一、 二叉平衡树

概念

二叉搜索树有称 二叉排序树,它也可以是一个空树。

  • 如果它的左子树不为空,则左子树上所有结点的值都小于根结点的值
  • 如果他的右子树不为空,则右子树上所有结点的值都大于根结点的值
  • 它的左右子树也分别是二叉搜索树
    在这里插入图片描述

由图可以看出:二叉搜索树中最左侧的节点是树中最小的节点,最右侧节点一定是树中最大的节点

在这里插入图片描述

采用中序遍历遍历二叉搜索树,可以得到一个有序的序列:

1 3 4 5 6 7 8 9 10

二叉搜索树的查找

若根结点不为空:
	如果根结点的值==target值 返回true
	如果根结点的值 > target值 在其左子树查找
	如果根结点的值 < target值 在其右子树查找
否则 返回false	

二叉树查询效率【性能分析】

最优情况:二叉搜索树为完全二叉树,其平均比较次数 在这里插入图片描述

最差情况:二叉搜索树退化成单支树,其平均比较次数在这里插入图片描述

二、 AVL树【高度平衡的二叉搜索树】

概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

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

下面说一下二叉搜索树的特点:

  • 它的左子树右子树都是AVL树。
  • 左右子树高度之差的绝对值不超过 1

在这里插入图片描述

如果一颗二叉搜索树是高度平衡的,他就是AVL树。它有n个结点,其高度可保持在 O(log2N),搜索时间复杂度O( log2N)

AVL树结点的定义

为了AVL树实现简单,AVL树节点在定义时维护一个平衡因子,具体节点定义如下:
在这里插入图片描述

static class TreeNode {
        public int val;
        public int bf; //平衡因子
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;

        public TreeNode(int val) {
            this.val = val;
        }
    }

AVL树的插入

public TreeNode root;

    public boolean insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return true;
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val == val) {
                return false;
            } else {
                parent = cur;
                cur = cur.left;
            }
        }
        //cur == null
        if (parent.val < val) {
            parent.right = node;
        } else {
            parent.left = node;
        }
        //以上是将数据插入到AVL树中 【和二叉搜索树一样】
        //平衡因子 = 右子树高度 - 左子树高度
        node.parent = parent;
        //插入之后,根据平衡因子来进行树的调整
        cur = node;
        //调节平衡因子
        while (parent!=null) {
            //先看cur是parent的左还是右 决定平衡因子是++ 还是 --
            if (cur == parent.right) {
                parent.bf++;
            } else {
                parent.bf--;
            }
            //检查当前的平衡因子 是不是绝对值 1 0 -1
            if (parent.bf == 0) {
                //说明平衡
                break;
            } else if (parent.bf == 1 || parent.bf == -1) {
                //1 和 -1 代表当前分支树平衡 不代表整个树平衡
//                继续向上判断 修改平衡因子
                cur = parent;
                parent = cur.parent;

            } else {
                if (parent.bf == 2) {
//                    parent.bf == 2 右树高 需要降低右树的高度

                    if (cur.bf == 1) {
                        rotateLeft(parent);

                    } else {
//                        parent.bf == -1
                        //右左双旋

                    }
                } else {
//                    parent.bf == -2 左树高 需要降低左树的高度
                    if (cur.bf == -1) {
                        //右旋
                        rotateRight(parent);
                    } else {
//                        parent.bf == 1
                        //左右双旋
                        rotateLR(parent);

                    }
                }
                break;
            }
        }
        return false;
    }

AVL树的旋转

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

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

左右双旋

在这里插入图片描述

右左双旋

在这里插入图片描述

旋转代码:

   //右单旋
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        if (subLR != null) {
            subLR.parent = parent;
        }
        subL.right = parent;
//记录下当前parent的父亲
        TreeNode parParent = parent.parent;
        parent.parent = subL;
        if (parent == root) {
        //独立的子树
            root = subL;
            root.parent = null;//subL.parent = null;
        } else {
//不是独立的子树,是其他树的左树或者右树
            if (parParent.left == parent) {
//                2. 新节点插入较高右子树的右侧---左单旋
//                左单旋的实现,学生们可以参考右单旋的实现。
                parParent.left = subL;
            } else {
                parParent.right = subL;
            }
            subL.parent = parParent;
        }
        subL.bf = 0;
        parent.bf = 0;

    }

    // 左单旋
    private void rotateLeft(TreeNode parent) {
        //subR为parent的右树
        TreeNode subR = parent.right;
        //subRL为subR的左树
        TreeNode subRL = subR.left;
        //1、第一个修改的指向
        parent.right = subRL;
        //2、第2个修改的指向有可能subRL这棵树是空树
        if (subRL != null) {
            subRL.parent = parent;
        }
        //3、第2个修改的指向
        subR.left = parent;
         //记录下来 之前的parent的父亲
        TreeNode parentParent = parent.parent;
        //4、第4个修改的指向
        parent.parent = subR;
        //5、有可能当前父亲节点,不是根
        if (parent == root) {
            root = subR;
            root.parent = null;
        } else {
            if (parent == parentParent.left) {
                parentParent.left = subR;
                subR.parent = parentParent;
            } else {
                parentParent.right = subR;
                subR.parent = parentParent;
            }
        }
        subR.bf = parent.bf = 0;
    }
    //左右双旋
    private void rotateLR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;
        rotateLeft(parent.left);
        rotateRight(parent);
        if(bf == -1) {
            subL.bf = 0;
            parent.bf = 1;
            subLR.bf = 0;
        }else if(bf == 1) {
            subL.bf = -1;
            parent.bf = 0;
            subLR.bf = 0;
        }
    }

    //右左双旋
    private void rotateRL(TreeNode parent) {
       TreeNode subR = parent.right;
       TreeNode subRL = subR.left;
       int bf = subRL.bf;

       rotateRight(parent.right);
       rotateLeft(parent);

       if (bf == 1){
           parent.bf = -1;
           subR.bf = 0;
           subRL.bf = 0;
       }else if(bf == -1){
           parent.bf = 0;
           subR.bf = 0;
           subRL.bf = 1;
       }
    }

AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
    比特就业课
  2. 验证其为平衡树
  • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
  • 节点的平衡因子是否计算正确

验证当前树是不是 AVL树,如果是,该怎么验证? 【高度平衡二叉搜索树】

在这里插入图片描述

//中序遍历的结果是有序的 就能说明当前树 一定是AVL树吗? 不一定的
    public void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        System.out.println(root.val + " ");
        inorder(root.right);
    }
    private int height(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftH = height(root.left);
        int rigthH = height(root.right);

        return leftH>rigthH?leftH+1:rigthH+1;
    }
    private boolean isBalance(TreeNode root) {
        if(root == null) {
            return true;
        }
        int leftH = height(root.left);
        int rightH = height(root.right);
        //检查平衡因子
        if(rightH - leftH != root.bf) {
            System.out.println(root.val+" 平衡因子异常!");
            return false;
        }
        return Math.abs(leftH-rightH) <= 1 && isBalance(root.left) && isBalance(root.right);
    }

测试:
在这里插入图片描述

AVL树的删除

删除步骤:

  • 找到需要删除的结点
  • 按照搜索二叉树的删除规则删除结点
  • 更新平衡因子,如果出现不平衡,进行旋转 – 单旋/双旋

AVL树的查找效率【性能分析】

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即在这里插入图片描述

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

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

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

相关文章

【Acwing 周赛#82】AcWing 4783. 多米诺骨牌

目录 4782.第k个数 4783. 多米诺骨牌 - bfs 记录时间 4782.第k个数 java大顶堆 import java.util.*;public class Main {public static void main(String[] args ){Scanner scnew Scanner(System.in);int nsc.nextInt(),ksc.nextInt();k--;PriorityQueue<Integer> qn…

HarmonyOS玩转ArkUI动效 - 水母动画

前言 本文会详细讲解我参加&#xff1a; HarmonyOS【挑战赛第三期】玩转ArkUI动效的项目 我的参赛项目源码&#xff1a;【挑战赛第三期】JellyfishAnimation 动画效果参考自&#xff1a;cassie-codes的水母SVG 华为鸿蒙已经放弃Java作为鸿蒙的开发语言&#xff0c;开发了一个申…

基于java+springmvc+mybatis+vue+mysql的校运会管理系统小程序

项目介绍 运动是伴随人类一生的一种行为和活动&#xff0c;只有不断的运动才能够彰显生命的意义&#xff0c;尤其是当代的学生&#xff0c;课业繁重往往忽略了体育锻炼&#xff0c;为了能够提高学子们对体育运动的积极性&#xff0c;基本所有的高校每年都会定期的举办运动会。…

软件设计师常考知识点

絮絮叨叨&#xff1a;哈喽大家好&#xff5e;这里是一口八宝周[送花花]。前段时间闲来无事报考了今年的软件设计师考试&#xff0c;觉得凭借自己“自律”的学习&#xff0c;一定可以把书看完&#xff0c;把题刷完顺利上岸&#x1f60e;。 书确实没看完&#xff0c;但是视频学完…

MySQL or条件命中

需求如下&#xff1a;当写入SQL语句中有任意一个字段在数据库中存在时&#xff0c;不可写入&#xff0c;并返回具体的重复字段。 使用Java Steam处理数据集循环执行SQL需要多次执行SQL&#xff0c;适合单条件索引的情况下使用&#xff0c;现状是想执行少量的SQL实现需求&#…

操作系统,计算机网络,数据库刷题笔记12

操作系统&#xff0c;计算机网络&#xff0c;数据库刷题笔记12 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xf…

u盘/移动硬盘的视频文件出现损坏怎么办?修复损坏视频办法分享!

一般情况下&#xff0c;视频文件都是比较大&#xff0c;如果直接存放于电脑&#xff0c;就会占用比较大的存储空间。不少小伙伴都会把它存放于U盘或者移动硬盘&#xff0c;而且作为一种便携式硬盘&#xff0c;可以在各电脑之间使用&#xff0c;非常方便。但这也造成文件很容易出…

【OpenCV-Python】教程:6-4 Depth Map from Stereo Images 立体图像的深度图

OpenCV Python Depth Map from Stereo Images 立体图像的深度图 【目标】 通过立体图像创建一个深度图 【理论】 上一节中&#xff0c;我们学习了一些基本概念&#xff0c;如对极约束和其他一些相关术语。我们还可以看到&#xff0c;如果我们有同一个场景的两张图像&#x…

无尘室中高效过滤器的更换时间

广州特耐苏净化设备有限公司详细介绍无尘室中高效过滤器使用多久需要更换 为了保证产品的生产质量和人员工作环境的舒适性&#xff0c;无尘室对环境的湿度、温度、新鲜空气量、状态、照明等都有严格的规定。无尘室系统一般配备三级过滤器的空气净化系统&#xff0c;采用初效、…

西门子博途与上位机TCPIP通信

1、PLC硬件IP设定及组态如下图&#xff1a; 堆垛机 1号机 IP地址&#xff1a;190.20.0.72 掩码 255.255.255.0 2、PLC与上位机TCP网络连接组态如下图&#xff1a; WCS上位机IP地址设定 IP地址&#xff1a;190.20.0.&#xff12;&#xff15;&#xff10; 掩码 255.255.255.0…

[强网杯 2019]Upload

一、信息收集 打开界面是一个登陆&#xff0c;界面随便注册一个然后登陆 然后是一个上传文件的操作&#xff0c;点击php后发现的回显 然后又上传了一个图片文件&#xff0c;显示出了照片的存放路径&#xff0c;这里可以上传图片码 然后抓包看一下&#xff0c;base64解码 a:5:{…

想要学习次世代3d建模,需要用到哪些软件,制作流程是什么?

在校大学生想要学习游戏次世代建模&#xff0c;首先就要先了解次世代是什么&#xff1f;制作建模所需要用到的软件有哪些&#xff0c;随着次世代游戏的不断发展&#xff0c;游戏美术制作流程也迎来了全新的制作方式&#xff0c;像ZBrush、SP等软件就解放了我们的双手和制作方式…

Spark-Spark Sql(DataFrame、DataSet、Scala代码开发、数据的加载和保存)

文章目录Spark SqlHive and SparkSQL特点DataFrame 是什么DataSet 是什么核心编程新的起点DataFrame创建SQL语法DSL 语法RDD > DataFrameDataFrame > RDDDataSet创建RDD > DataSetDataSet > RDDDataFrame > DataSetDataSet > DataFrameRDD、DataFrame、DataS…

CSND近期推出的猿如意到底有没有必要安装

可能很多人还不知道猿如意是什么&#xff0c;先给大家科普一下 猿如意 工具代码&#xff0c;一搜就有 程序员的如意兵器 猿如意是一款面向开发者的辅助开发工具箱&#xff0c;包含了效率工具、开发工具下载&#xff0c;教程文档&#xff0c;代码片段搜索&#xff0c;全网搜索等…

Raydium被盗造成巨额损失,但Zebec Protocol以及$ZBC并未受影响

在12月17日&#xff0c;Solana上最大的DEX Raydium因木马攻击导致流动性资金池所有者帐户的私钥泄露&#xff0c;攻击者访问了资金池所有者帐户&#xff0c;然后能够调用withdraw pnl函数&#xff0c;该函数用于收集池中掉期所赚取的交易/协议费用。 而受影响的资金池包括SOL-…

论文翻译:LiDAR Based Negative Obstacle Detection for Field Autonomous Land Vehicles

论文地址&#xff1a;https://onlinelibrary.wiley.com/doi/full/10.1002/rob.21609?saml_referrer &#xff08;机翻&#xff0c;自己保存观看的&#xff09; Abstract&#xff1a; 野外自动驾驶陆地车辆&#xff08;ALV&#xff09;的负障碍是指沟渠、坑或具有负坡度的地形&…

在Vue3这样子写页面更快更高效

前言 在开发管理后台过程中&#xff0c;一定会遇到不少了增删改查页面&#xff0c;而这些页面的逻辑大多都是相同的&#xff0c;如获取列表数据&#xff0c;分页&#xff0c;筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。 对于刚开始只有 1&#xff…

【JavaScrip】for循环

文章目录for循环案例1&#xff1a;两数相加案例2&#xff1a;绘制九九乘法表案例3&#xff1a;水仙花数案例4&#xff1a;绘制菱形案例5:计算表达式的结果break和continuefor循环 for循环理解成循环的一种简洁(结构)的写法. 语法结构&#xff1a; for(初始化;限制条件;变量值的…

vue封装背景知识小插曲之插槽slot的用法

vue封装背景知识小插曲之插槽slot的用法一 什么是插槽slot&#xff0c;都可以干啥&#xff1f;二 常见的插槽用法一 什么是插槽slot&#xff0c;都可以干啥&#xff1f; 直白点说就是子组件使用<slot> 先占了个地方&#xff0c;然后当父组件使用子组件的时候&#xff0c;…

第十三章 半监督学习

13.1 未标记样本 事实上&#xff0c;未标记样本虽未直接包含标记信息&#xff0c;但若它们与有标记样本是从同样的数据源独立同分布采样而来&#xff0c;则它们所包含的关于数据分布的信息对建立模型将大有裨益。下图给出一个直观的例示。若仅基于图中的一个正例和一个反例&am…