Java——AVL树

news2024/11/19 7:27:40

平衡二叉树

在之前的blog中讲到,平衡二叉树是一棵树,任意一个节点的左树的所有节点都小于这个节点,右树的所有节点都大于这个节点
因此,可以利用这个性质来中序遍历,就可以得到一个有序的序列,而如果我们要查找某一个值所在的节点,如果这个平衡二叉树的每个子树的左右子树高度接近,那么就会和二分查找一样,每经过一个节点就会直接淘汰一半的值,因此时间复杂度就是log以2为底的n
但是,由于并不是所有平衡二叉树都是左右子树高度接近,因此,假如出现了一棵平衡二叉树所有节点都是只有右子树,那么查找速度就会退化为链表
为了避免这种情况发生,AVL树应运而生

AVL树

是一颗平衡二叉树,并且满足左右子树的高度差不超过1
这样的话,我们的查找效率就得到了保证

平衡因子

就是用来计算二叉树左右子树的高度差的
平衡因子 = 右子树高度 - 左子树高度

节点定义

在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树需要时刻满足左右子树的高度差不大于1,那么在插入一个元素时就需要不断的调整

首先按照平衡二叉树的方法进行插入
先判断根节点是否为空,为空则直接插入
然后定义一个cur和parent,cur先为root,cur一直向下遍历,如果val小于cur.val,cur就变成cur.left,大于的话就变成cur.right,如果等于,就说明已经有这个节点了,return false,在cur遍历的过程中,使parent始终为cur的上一个节点,使得当cur变成空时,能够记录上一个节点的位置
然后判断val和parent.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){
                parent = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }
        if(parent.val < val){
            parent.right = node;
        } else {
            parent.left = node;
        }
        node.parent = parent;
        cur = node;

接下来,让cur重新变成插入的节点,parent变成插入节点的父节点,然后就应该调整树的结构,修改平衡因子,分为以下几种情况

如果插入节点是右子树,那么说明父亲节点的平衡因子应该++,否则就应该–

如果parent的bf已经是0了,那么上面也就不需要继续调整了,可以直接break

如果parent的bf是1或者-1,说明其子树已经平衡了,但是上面的结构不一定平衡,因此需要向上迭代,让cur为parent,parent为parent的parent

如果parent的平衡因子是2,cur的平衡因子是1,也就是下图的情况,那么需要左单旋(后面讲)
在这里插入图片描述

如果parent的平衡因子是2,cur的平衡因子是-1,也就是下图的情况,那么需要右左双旋(后面讲)
在这里插入图片描述

如果parent的平衡因子是-2,cur的平衡因子是-1,也就是下图的情况,那么需要右单旋(后面讲)
在这里插入图片描述

如果parent的平衡因子是-2,cur的平衡因子是1,也就是下图的情况,那么需要左右双旋,也就是先左旋,再右旋(后面讲)
在这里插入图片描述

//调节平衡因子
        while(parent != null){
            //判断cur是parent的左还是右,决定parent的平衡因子变换
            if(cur == parent.right){
                parent.bf++;
            } else {
                parent.bf--;
            }

            if(parent.bf == 0){
                //整个树已经平衡
                break;
            } else if (parent.bf == 1 || parent.bf == -1){
                //子树平衡了,继续向上判断平衡因子
                cur = parent;
                parent = cur.parent;
            }else {
                if(parent.bf == 2){
                    //右树高,降低右树的高度
                    if(cur.bf == 1){
                        //左单旋
                        rotateLeft(parent);
                    } else {
                        //cur.bf == -1
                        rotateRL(parent);
                    }
                } else {
                    //左树高,降低左树的高度
                    //parent.bf = -2
                    if(cur.bf == -1){
                        //右单旋
                        rotateRight(parent);
                    } else {
                        //cur.bf == 1
                        rotateLR(parent);
                    }
                }

                //调整后就平衡了
                break;
            }
        }
        return true;

而对于循环条件,此循环是为了从下到上调节所有与插入节点有关的节点的平衡因子,因此需要向上迭代,最终的终止条件就是cur迭代到根,parent迭代到空

左单旋

定义parent的右孩子为subR,subR的左孩子为subRL,并依据图上关系重新组织其之间的结构。判断parent是不是根节点,如果为根就让subR为根,如果不为根,就让parent的父节点的孩子指针指向subR

然后更改平衡因子,按照图上关系,subR的平衡因子应该改为0,parent的平衡因子应该改成0(subRR的平衡因子在第一次迭代的时候就改过了)
在这里插入图片描述

private void rotateLeft(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRl = subR.left;

        parent.right = subRl;
        subR.left = parent;
        if(subRl != null){
            subRl.parent = parent;
        }

        TreeNode pParent = parent.parent;

        parent.parent = subR;

        if(root == parent){
            root = subR;
            root.parent = null;
        } else {
            if (pParent.left == parent){
                pParent.left = subR;
            } else {
                pParent.right = subR;
            }
            subR.parent = pParent;
        }

        parent.bf = 0;
        subR.bf = 0;
    }

右单旋

定义parent的左孩子为subL,subL的右孩子为subLR,并依据图上关系重新组织其之间的结构。判断parent是不是根节点,如果为根就让subL为根,如果不为根,就让parent的父节点的孩子指针指向subL

然后更改平衡因子,按照图上关系,subL的平衡因子应该改为0,parent的平衡因子应该改成0(subLL的平衡因子在第一次迭代的时候就改过了)
在这里插入图片描述

private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;

        parent.left = subLR;
        subL.right = parent;
        if(subLR != null) {
            subLR.parent = parent;
        }

        TreeNode pParent = parent.parent;

        parent.parent = subL;

        //检查是否parent为根节点
        if(parent == root){
            //使subL为根节点
            root = subL;
            root.parent = null;
        } else {
            //使得parent的原parent节点的孩子为subL
            if (pParent.left == parent) {
                pParent.left = subL;
            } else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }

        //修改平衡因子
        parent.bf = 0;
        subL.bf = 0;
    }

右左双旋

定义parent的右孩子为subR,subR的左孩子为subRL,并记录subRL的平衡因子bf

右左双旋有两种情况:

情况一:
新节点插到subRL的右孩子处,也就是bf为1
在这里插入图片描述
情况二:
新节点插到subRL的左孩子处,也就是bf为-1
在这里插入图片描述
这两种情况的修改树结构的流程是一样的,但是在修改平衡因子时有所不同

先修改树的结构:
先让parent的右孩子进行右旋转,然后让parent进行左旋转

修改平衡因子:
判断bf为-1还是1还是0
如果是1,那么就是第一张图的情况,所以parent的平衡因子会修改成-1,subRL和subR的平衡因子会修改为0

如果是-1,那么就是第二张图的情况,所以subR的平衡因子会修改为1,subRL和parent的平衡因子是0

如果是0,就相当于没插新孩子,那么就不用修正平衡因子了

private void rotateRL(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        int bf = subRL.bf;

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

        //修改负载因子,依据subRL的负载因子的大小
        if(bf == -1){
            subR.bf = 0;
            subRL.bf = 1;
            parent.bf = 0;
        } else if (bf == 1){
            //bf == 1
            subR.bf = 0;
            subRL.bf = 0;
            parent.bf = -1;
        }
        //bf为0不需要修改平衡因子
    }

左右双旋

和右左双旋的思路大致相同

定义parent的左孩子为subL,subL的右孩子为subLR,并记录subLR的平衡因子bf

右左双旋有两种情况:
情况一:
新节点插到subRL的右孩子处,也就是bf为1
在这里插入图片描述

情况二:
新节点插到subRL的左孩子处,也就是bf为-1
在这里插入图片描述
这两种情况的修改树结构的流程是一样的,但是在修改平衡因子时有所不同

先修改树的结构:
先让parent的右孩子进行右旋转,然后让parent进行左旋转

修改平衡因子:
判断bf为-1还是1还是0
如果是1,那么就是第一张图的情况,所以subL的平衡因子会修改成-1,subLR和parent的平衡因子会修改为0

如果是-1,那么就是第二张图的情况,所以parent的平衡因子会修改为1,subLR和subL的平衡因子是0

如果是0,就相当于没插新孩子,那么就不用修正平衡因子了

private void rotateLR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;

        rotateLeft(parent.left);
        rotateRight(parent);

        //修改负载因子,依据subLR的负载因子的大小
        if(bf == -1){
            subL.bf = 0;
            subLR.bf = 0;
            parent.bf = 1;
        } else if (bf == 1){
            subL.bf = -1;
            subLR.bf = 0;
            parent.bf = 0;
        }
        //bf为0不需要修改平衡因子
    }

到这里,插入代码就写完了

删除节点

删除节点和平衡二叉树是一样的,先找到一个替罪的,然后交换位置后再删除,唯一设计到的就是调节平衡因子,具体方法和添加节点是一样的,这里就不详细讲解了

验证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 left = height(root.left);
        int right = height(root.right);
        return Math.max(left,right) + 1;
    }

判断是否为平衡二叉树

public boolean isBalance(TreeNode root){
    if (root == null){
        return true;
    }
    int leftHeight = height(root.left);
    int rightHeight = height(root.right);

    if(rightHeight - leftHeight != root.bf){
        System.out.println("次节点:" + root.val + "平衡因子异常!");
        return false;
    }

    return Math.abs(leftHeight - rightHeight) <= 1
            && isBalance(root.left)
            && isBalance(root.right);
}

如果平衡因子和高度不一致,那么可以直接终止判定,打印一个错误

验证代码

package AVLTree;

public class Test {
    public static void main(String[] args) {
        //int[] array = {16,3,7,11,9,26,18,14,15};
        int[] array = {4,2,6,1,3,5,15,7,16,14};
        AVLTree avlTree = new AVLTree();
        for (int i = 0; i < array.length; i++) {
            avlTree.insert(array[i]);
        }

        System.out.println(avlTree.isBalance(avlTree.root));
        avlTree.inorder(avlTree.root);
    }
}

通过上述代码,可以看到,我们的avl树的插入是正确的

完整插入和验证代码

package AVLTree;

import apple.laf.JRSUIUtils;

public class AVLTree {
    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;
        }
    }

    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){
                parent = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }
        if(parent.val < val){
            parent.right = node;
        } else {
            parent.left = node;
        }
        node.parent = parent;
        cur = node;

        //调节平衡因子
        while(parent != null){
            //判断cur是parent的左还是右,决定parent的平衡因子变换
            if(cur == parent.right){
                parent.bf++;
            } else {
                parent.bf--;
            }

            if(parent.bf == 0){
                //整个树已经平衡
                break;
            } else if (parent.bf == 1 || parent.bf == -1){
                //子树平衡了,继续向上判断平衡因子
                cur = parent;
                parent = cur.parent;
            }else {
                if(parent.bf == 2){
                    //右树高,降低右树的高度
                    if(cur.bf == 1){
                        //左单旋
                        rotateLeft(parent);
                    } else {
                        //cur.bf == -1
                        rotateRL(parent);
                    }
                } else {
                    //左树高,降低左树的高度
                    //parent.bf = -2
                    if(cur.bf == -1){
                        //右单旋
                        rotateRight(parent);
                    } else {
                        //cur.bf == 1
                        rotateLR(parent);
                    }
                }

                //调整后就平衡了
                break;
            }
        }
        return true;
    }

    /**
     * 先右旋,再左旋
     * @param parent
     */
    private void rotateRL(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        int bf = subRL.bf;

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

        //修改负载因子,依据subRL的负载因子的大小
        if(bf == -1){
            subR.bf = 1;
            subRL.bf = 0;
            parent.bf = 0;
        } else if (bf == 1){
            //bf == 1
            subR.bf = 0;
            subRL.bf = 0;
            parent.bf = -1;
        }
        //bf为0不需要修改平衡因子
    }

    /**
     * 先左旋,后右旋
     * @param parent
     */
    private void rotateLR(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;

        rotateLeft(parent.left);
        rotateRight(parent);

        //修改负载因子,依据subLR的负载因子的大小
        if(bf == -1){
            subL.bf = 0;
            subLR.bf = 0;
            parent.bf = 1;
        } else if (bf == 1){
            subL.bf = -1;
            subLR.bf = 0;
            parent.bf = 0;
        }
        //bf为0不需要修改平衡因子
    }

    /**
     * 左单旋
     * @param parent
     */
    private void rotateLeft(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRl = subR.left;

        parent.right = subRl;
        subR.left = parent;
        if(subRl != null){
            subRl.parent = parent;
        }

        TreeNode pParent = parent.parent;

        parent.parent = subR;

        if(root == parent){
            root = subR;
            root.parent = null;
        } else {
            if (pParent.left == parent){
                pParent.left = subR;
            } else {
                pParent.right = subR;
            }
            subR.parent = pParent;
        }

        parent.bf = 0;
        subR.bf = 0;
    }

    /**
     * 右单旋
     * @param parent
     */
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;

        parent.left = subLR;
        subL.right = parent;
        if(subLR != null) {
            subLR.parent = parent;
        }

        TreeNode pParent = parent.parent;

        parent.parent = subL;

        //检查是否parent为根节点
        if(parent == root){
            //使subL为根节点
            root = subL;
            root.parent = null;
        } else {
            //使得parent的原parent节点的孩子为subL
            if (pParent.left == parent) {
                pParent.left = subL;
            } else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }

        //修改平衡因子
        parent.bf = 0;
        subL.bf = 0;
    }

    /**
     * 中序遍历
     * @param root
     */
    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 left = height(root.left);
        int right = height(root.right);
        return Math.max(left,right) + 1;
    }

    /**
     * 判断是不是平衡二叉树
     * @param root
     * @return
     */
    public boolean isBalance(TreeNode root){
        if (root == null){
            return true;
        }
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);

        if(rightHeight - leftHeight != root.bf){
            System.out.println("次节点:" + root.val + "平衡因子异常!");
            return false;
        }

        return Math.abs(leftHeight - rightHeight) <= 1
                && isBalance(root.left)
                && isBalance(root.right);
    }
}

分析

可以看到,我们的avl树的插入过程十分复杂,如果改动一个数据就需要从下到上重新组织数据,因此avl树只适合查找数据,并不适合频繁的插入删除数据

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

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

相关文章

谷歌地图商家抓取工具 G-Business Extractor 7.5

G 业务提取器 | 谷歌地图抓取工具 G-Business Extractor是一款功能强大的工具&#xff0c;可帮助您从 Google 地图中寻找商机。它是最好的Google Maps Scraper工具&#xff0c;能够从最重要的企业目录中提取数据。 Google 地图是一个来源&#xff0c;您可以在其中找到按类别和位…

“美亚杯”第三届中国电子数据取证大赛答案解析(团体赛)

Questions Gary被逮捕后,其计算机被没收并送至计算机取证实验室。经调查后&#xff0c;执法机关再逮捕一名疑犯Eric&#xff0c;并检取其家中计算机(window 8), 并而根据其家中计算机纪录, 执法机关再于其他地方取得一台与案有关的服务器,而该服务器内含四个硬盘。该服务器是运…

跨端开发浪潮中的变与不变

自 90 年代初开启 PC 时代以来&#xff0c;随着移动网络的快速普及&#xff0c;在 2010 年左右&#xff0c;进入移动时代、IOT 时代&#xff0c;各种移动互联设备不断涌现&#xff0c;除了最常见的 PC、Pad、智能手机外&#xff0c;它还可能是小小的一块智能手表&#xff0c;也…

NKOJ P9492 【USACO】视频共享

题目分析 这道题其实最容易想到的方法是离线枚举&#xff0c;但是其时间复杂度为,很明显会超时&#xff08;这题数据连离线化都救不了&#xff09; 那该如何办&#xff1f; 并查集&#xff1b;因为他说有一个推荐列表&#xff0c;而这个推荐列表中i号视频推荐了j号视频&#…

SpringSecurity+OAuth2.0+JWT实现单点登录应用

SpringSecurityOAuth2.0JWT实现单点登录应用 gitee项目练习地址&#xff1a;https://gitee.com/xzq25_com/springsecurity.oauth2 OAuth2.0单点登录实践一、搭建OAuth授权服务器&#xff0c;采用授权码模式JWT令牌二、创建服务client&#xff1a;SSOA、SSOB 并进行测试一、搭建…

【云计算与大数据技术】大数据系统总体架构概述(Hadoop+MapReduce )

一、总体架构设计原则 企业级大数据应用框架需要满足业务的需求&#xff0c;一是要求能够满足基于数据容量大&#xff0c;数据类型多&#xff0c;数据流通快的大数据基本处理需求&#xff0c;能够支持大数据的采集&#xff0c;存储&#xff0c;处理和分析&#xff0c;二是要能…

openGauss数据库的使用

目录前言1. 启动/停止/重启数据库&#xff08;1&#xff09;极简版启动/停止/重启命令&#xff08;2&#xff09;企业版启动/停止/重启命令2. 登录数据库&#xff08;1&#xff09;登录数据库时的基本连接参数&#xff08;2&#xff09;登录数据库时的常用连接参数&#xff08;…

如何使用轻量应用服务器搭建高颜值的YesPlayMusic网易云播放器

本文介绍了如何使用腾讯云的Lighthouse轻量应用服务器来搭建一个高颜值的第三方网易云播放器。 ​ 项目简介 本文使用的是YesPlayMusic项目&#xff0c;这是一款高颜值的第三方网易云播放器&#xff0c;它完全可以作为网易云官方应用的替代品。而且还拥有一些网易云官方应用没…

react-dnd 拖拽能力教程

前言 近几年来&#xff0c;低代码、零代码的热度在国内逐年递增。“复杂度同力一样不会消失&#xff0c;也不会凭空产生&#xff0c;它总是从一个物体转移到另一个物体或一种形式转为另一种形式”。用户在使用低零代码构建应用程序时&#xff0c;这些能力只是被平台研发人员提…

SQL SERVER 2019卸载和安装

卸载过程删除SQL Server2019包括sql server这个数据库和它的管理工具SQLServer Management Studio以及他们的注册表信息和安装的目录&#xff0c;以上&#xff0c;最重要的是一定要有耐心&#xff0c;一步一步慢慢来。 首先打开一定要把SQL的服务都关掉&#xff0c;这个很重要…

压缩包文件如何设置加密、删除加密?

压缩包是将文件压缩成RAR、ZIP格式文件&#xff0c;将文件压缩成压缩包之后&#xff0c;就更方便转发以及保存&#xff0c;而且压缩包文件可以进行加密&#xff0c;这样也能够起到对文件的保护作用&#xff0c;今天和大家分享如何对压缩包进行加密以及如何删除压缩包密码。 压…

战略,就没一本好书

战略这个词被工商业界引爆&#xff0c;都是1965年的事。想来到现在已经快60年了。但是实话说&#xff0c;战略这么重要的事&#xff0c;其实没几本好书&#xff0c;也就是说&#xff0c;这个领域&#xff0c;实在没什么有效的研究成果。&#xff08;1&#xff09;起点1965年是个…

Vue 进阶二 | 系统性学习 | 无知的我费曼笔记

无知的我正在复盘Vue 该笔记特点是 重新整理了涉及资料的一些语言描述、排版而使用了自己的描述对一些地方做了补充说明。比如解释专有名词、类比说明、对比说明、注意事项提升了总结归纳性。尽可能在每个知识点上都使用一句话 || 关键词概括更注重在实际上怎么应用提出并回答…

数据可视化之卡塔尔世界杯,世界杯8强全部出炉,你看好那支队伍?

2022年下半年可算是集结了众多国际赛事&#xff0c;前有csgo major&#xff0c;英雄联盟总决赛&#xff0c;后有斯诺克英锦赛。当然这些赛事里面最万众瞩目的就是4年一度的卡塔尔世界杯了。本届世界杯开赛前最大的看点就是世界杯的花费&#xff0c;卡塔尔2022年世界杯花费2290亿…

他让我看重采样

周末邓总让我帮忙看下重采样的代码&#xff0c;然后我就用上了自己的神器。我的神器就是Google之后总结了下代码&#xff0c;完整的代码可以往下看&#xff0c;我们平时也会用到重采样&#xff0c;通道转换、交织和非交织的相互转换、给音频重新map等等。这些都是做音频需要搞的…

(红帽系统)redhat7.2 相关服务器配置

远程连接服务器配置 简介 使用SSH可以在本地主机和远程服务器之间进行加密的传输数据&#xff0c;实现数据的安全传输。而OpenSSH是SSH协议的免费开源实现&#xff0c;它采用安全加密的网络连接工具代替了telnet、ftp等 实现步骤 第一步 进入红帽系统 第二步 检查安装系统时…

需求处理的流程及问题挑战

本文主要讲需求的一般处理流程&#xff0c;以及可能存在的问题及挑战。 一、需求处理流程&#xff1a; 1、需求的生命周期&#xff1a; 起点是提需求&#xff0c;终点是拒绝或接受需求。每个人希望自己的需求能被接受或满足&#xff0c;但资源总是有限的。每个需求从产生到实…

SRM系统的国内品牌前五名是哪几家,大概价位是多少?

SRM系统的国内品牌前五名是哪几家&#xff0c;大概价位是多少&#xff1f;SRM系统是采购数字化转型过程中的产物&#xff0c;SRM系统与ERP与SCM系统打通后&#xff0c;能够破除信息壁垒&#xff0c;增加采购部门与业务部门的沟通效率&#xff0c;从而实现企业人、物、财成本的降…

JavaSE——多线程详细

目录 一、多线程 1.1 基本介绍 1.2 进程和线程的关系 1.3 多线程并发概念 二、实现线程的方式 2.1 继承Thread类 2.2 实现java.lang.Runnable接口 2.3 匿名类 2.4 实现Callable接口&#xff08;JDK8新特性&#xff09; 2.5 run和start的区别 2.6 线程声明周期 三、…

双非本科怎么了,照样拿到阿里 offer! 分享阿里技术四面 + 交叉面 +HR 面难忘经历

说一下 java 类加载器的工作机制&#xff1f;类加载在哪个区域进行的&#xff1f; 说一下 java 的线程模型&#xff1f; violate 了解吗&#xff1f;它的原理是什么&#xff1f;violate 是线程安全的吗&#xff1f; 保证线程安全的解决方法有哪些&#xff1f;说一说读写锁吧…