HashMap红黑树源码解读

news2024/10/3 2:18:21
  1. 链表转换为红黑树节点

当往hashMap中添加元素,在同一个hash槽位挂载的元素超过8个后,执行treeifyBin方法。

在treeifyBin方法中,只有当tab数组(hash槽位)的长度不小于MIN_TREEIFY_CAPACITY(默认64)时,才会将(n - 1) & hash位置处的所有节点树化。

先遍历(n-1)&hash处的链表,从头到尾,按顺序,将所有节点转换为treeNode。

  1. 树节点重新挂载

在将链表所有节点转换为TreeNode后,再执行treeify方法。

在treeify方法中,顺着头节点遍历链表设置红黑树(this是头节点)。

在遍历节点时,比较大小决定挂在红黑树的哪个节点上(先比hash,再使用Comparable接口的compareTo方法进行比较,小的挂在左边,大的挂在右边)。

  1. 红黑树平衡插入

在二叉树上新增节点后,执行balanceInsertion方法对树结构进行调整,以便符合红黑树定义。

下面代码中的注释是我推演加上的,若有错误,请指正。

        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
            x.red = true;
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                // x is top
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                // 新节点挂在root下
                else if (!xp.red || (xpp = xp.parent) == null)
                    return root;
                // 新节点挂在树左边,左子节点
                if (xp == (xppl = xpp.left)) {
                    // 新节点(1)挂在最左的左边,不需旋转,只需改变颜色
                    //       black(3)                  red(3)
                    //   red(2)    red(4)    =>   black(2)   black(4)
                    // red(1)                   red(1)
                    if ((xppr = xpp.right) != null && xppr.red) {
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else { // 新节点(2)挂在最左的右边
                        //       black(3)                      black(3)
                        //  red(1)         rotateLeft=>   red(2)    
                        //        red(2)              red(1) 
                        if (x == xp.right) {
                            root = rotateLeft(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            //       black(3)            black(3) xpp       
                            //    red(2)      =>     black(2) xp  
                            // red(1)              red(1) x
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                //        red(3)                 
                                //    black(2)      =>  black(2)
                                // red(1)           red(1)    red(3)
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else { // 新节点(4)挂在树最右的右边 xp != (xppl = xpp.left)
                    //       black(2)                  red(2)
                    //   red(1)    red(3)    =>   black(1)  black(3)
                    //                red(4)                    red(4)
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        // 新节点(2)挂在树最右的左边 
                        // black(1)           black(1)    
                        //        red(3)  =>       red(2)  
                        //    red(2)                    red(3)
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                // red(1)       
                                //    black(2)    =>    black(2)
                                //         red(3)    red(1)   red(3)
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

分析下balanceInsertion的思路:二叉树新增节点导致不符合红黑定义时,通过改变颜色和旋转(左旋或右旋)使得红黑树再次平衡,红黑树概念及旋转可参考https://blog.csdn.net/weixin_43790276/article/details/106042360。

根据插入节点是挂在左子节点(xp == (xppl = xpp.left)),还是右子节点,分情况进行了操作,下面是挂在左子节点时的三种情况。

下面为挂在右子节点时的三种情况,这六种情况,覆盖了所有插入时会导致的红黑树结构破坏。

新节点挂载并重新平衡红黑树后,将root节点移动到hash槽位的最前面

  1. rotateLeft左旋方法

简单说下,为什么要旋转节点,因为往二叉树中挂载新节点时,未严格按照红黑树的规定挂载(按照规定挂载会很麻烦),所以先按二叉树的规则挂载,再通过左旋、右旋、改变颜色来使其符合红黑树规定。

什么是左旋,其实主要是为了把旋转节点与它的右子节点进行位置互换,旋转节点相对于右子节点来说,它往左边移动了,所以叫左旋;右旋类似,这样不管二叉树是怎么样的,总可以通过左旋、右旋,挪动它们的位置,再加上改变颜色,使其达到红黑树的形态。

        // root:根节点 p:当前旋转节点
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
            // r:右子节点 pp:父父节点 rl:右左节点
            TreeNode<K,V> r, pp, rl;
            // 必须当前节点和右子节点存在才左旋
            if (p != null && (r = p.right) != null) {
                if ((rl = p.right = r.left) != null)
                    // 把右左节点挂在当前节点右边,见下面的左旋情况2
                    rl.parent = p;
                if ((pp = r.parent = p.parent) == null)
                    // 右节点挪到上方,变成根节点,见下面的左旋情况2
                    (root = r).red = false;
                else if (pp.left == p)
                    // 右子节点移到父父节点左边,见下面的左旋情况5
                    pp.left = r;
                else
                    // 右子节点移到父父节点右边,见下面的左旋情况6
                    pp.right = r;
                // 右子节点移到旋转节点上方,见下面的左旋情况1
                r.left = p;
                p.parent = r;
            }
            return root;
        }

上述左旋方法覆盖了6种二叉树左旋情况(注意二叉树有时并不能通过一次左旋就能变为红黑树,有时需要多次左旋或右旋及改变颜色),转换过程覆盖的情况画图演示如下。

左旋情况1,最简单的左旋,旋转节点p为1。

左旋情况2,旋转节点p为1。

左旋情况3,旋转节点p为1。

左旋情况4,旋转节点p为2。

左旋情况5,旋转节点p为4。

左旋情况6,旋转节点p为2。

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

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

相关文章

modbus协议

1 MODBUS 应用层报文传输协议 ADU应用数据单元PDU协议数据单元MBMODBUS协议MBAPMODBUS协议 ADU&#xff1a;地址域 PDU 差错校验PDU&#xff1a;功能码数据 串行链路&#xff1a; 最大RS485 ADU 256 字节PDU 256 - 服务器地址&#xff08;1字节&#xff09;- CRC&#xf…

Linux学习(7)文件权限与目录配置

目录 1. 使用者与群组 1&#xff0c;文件拥有者 2&#xff0c;群组概念 3&#xff0c;其他人 Linux 用户身份与群组记录的文件 2.Linux 文件权限概念 Linux的文件属性 第一栏代表这个文件的类型与权限(permission)&#xff1a; 第二栏表示有多少档名连结到此节点(i-no…

linux CUDAtoolkit+cudnn+tensorrt 的安装

windows上 CUDAtoolkitcudnn的安装 CUDAtoolkitcudnn的安装 须知 use command ubuntu-drivers devices查看你的显卡类型和推荐的驱动版本百度 nvidia-driver-*** 支持的 cuda 或 去文档查找驱动(比如450&#xff0c;460)匹配的cuda版本 下载 网盘下载 https://www.aliyundr…

Python实现贝叶斯优化器(Bayes_opt)优化Catboost回归模型(CatBoostRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景贝叶斯优化器 (BayesianOptimization) 是一种黑盒子优化器&#xff0c;用来寻找最优参数。贝叶斯优化器是…

18523-47-2,3-Azidopropionic Acid,叠氮基丙酸,可以与炔烃发生点击化学反应

【中文名称】3-叠氮基丙酸【英文名称】 3-Azidopropionic Acid&#xff0c;3-Azidopropionic COOH【结 构 式】【CAS】18523-47-2【分子式】C3H5N3O2【分子量】115.09【纯度标准】95%【包装规格】1g&#xff0c;5g&#xff0c;10g【是否接受定制】可进行定制&#xff0c;定制时…

龙蜥开发者说:为爱发电!当一个龙蜥社区打包 Contributor 是怎样的体验?| 第16期

「龙蜥开发者说」第 16 期来了&#xff01;开发者与开源社区相辅相成&#xff0c;相互成就&#xff0c;这些个人在龙蜥社区的使用心得、实践总结和技术成长经历都是宝贵的&#xff0c;我们希望在这里让更多人看见技术的力量。本期故事&#xff0c;我们邀请了龙蜥社区开发者 Fun…

无线通信时代的新技术----信标( Beacon)

随着IT技术的发展&#xff0c;无线通信技术也在不断发展。 现已根据预期用途开发了各种无线通信技术&#xff0c;例如 NFC、WIFI、Bluetooth和 RFID。 车辆内部结构的复杂化和数字化&#xff0c;车载通信网络技术的重要性也越来越高。 一个典型的例子是远程信息处理。 远程信息…

注重邮件数据信息安全 保障企业稳步发展

近年来&#xff0c;世界各地的政府、银行、电信公司、制造业以及零售业等&#xff0c;不断发生数据泄密事件。 就企业而言&#xff0c;邮件数据很容易成为竞争对手或者诈骗者窃取的目标。 电子邮件是企业中一种重要的沟通工具但是随着网络攻击手段的不断升级&#xff0c;电子邮…

RN面试题

RN面试题1.React Native相对于原生的ios和Android有哪些优势&#xff1f;1.性能媲美原生APP 2.使用JavaScript编码&#xff0c;只要学习这一种语言 3.绝大部分代码安卓和IOS都能共用 4.组件式开发&#xff0c;代码重用性很高 5.跟编写网页一般&#xff0c;修改代码后即可自动刷…

关系数据库

关系的三类完整性约束实体完整性规则• 保证关系中的每个元组都是可识别的和惟一的 • 指关系数据库中所有的表都必须有主键&#xff0c;而且表中不允许存在如下记录&#xff1a;– 无主键值的记录– 主键值相同的记录• 原因&#xff1a;实体必须可区分• 就像实体-学生&#…

谷歌外推留痕,谷歌搜索留痕快速收录怎么做出来的?

本文主要分享谷歌搜索留痕的收录效果是怎么做的&#xff0c;让你对谷歌留痕技术有一个全面的了解。 本文由光算创作&#xff0c;有可能会被修改和剽窃&#xff0c;我们佛系对待这样的行为吧。 谷歌搜索留痕快速收录怎么做出来的&#xff1f; 答案是&#xff1a;通过谷歌蜘蛛…

XLSX.utils读取日期格式错误

表格中的时间为2023/2/16调用 XLSX.utils.sheet_to_json 读取到的时间为2/16/23时间格式不对-期待的时间格式为2023-02-16 00:00增加代码 cellDates: true, dateNF: "yyyy-MM-dd HH:mm" 解决问题readerData (rawFile) {this.loading truethis.isFile true // 流程结…

透射电镜测试样品的制备要求和方法

透射电镜&#xff08;Transmission Electron Microscope&#xff0c;TEM&#xff09;是一种高分辨率的显微镜&#xff0c;能够对样品进行高精度的成像和分析。为了得到高质量的TEM图像&#xff0c;样品制备是非常重要的。 ​ 样品选择 TEM样品应该是具有明确结构和化学成分的…

《分布式技术原理与算法解析》学习笔记Day21

分布式数据存储三要素 什么是分布式数据存储系统&#xff1f; 分布式存储系统的核心逻辑&#xff0c;就是将用户需要存储的数据根据某种规则存储到不同的机器上&#xff0c;当用户想要获取指定数据时&#xff0c;再按照规则到存储数据的机器中获取。 分布式存储系统的三要素…

苏州市软件行业协会第五届第四次理事会暨元宇宙专委会成立决议会在苏召开

2月17日&#xff0c;2022年度苏州市软件行业协会第五届第四次理事会暨苏州市软件行业协会元宇宙专委会成立决议会在西交利物浦大学顺利召开。会议选举西交利物浦大学担任苏州市软件行业协会元宇宙专委会第一届轮值会长单位。 苏州市工信局大数据处处长&#xff08;信息化和软件…

python+pytest接口自动化(1)-接口测试基础

接口定义一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a;API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或…

MySQL锁篇

文章目录说明&#xff1a;锁篇一、MySQL有那些锁&#xff1f;二、MySQL 是怎么加锁的&#xff1f;三、update 没加索引会锁全表&#xff1f;四、MySQL 记录锁间隙锁可以防止删除操作而导致的幻读吗&#xff1f;五、MySQL 死锁了&#xff0c;怎么办&#xff1f;六、字节面试&…

【单例模式】单例模式创建的几种方式

一、饿汉模式饿汉模式是在类加载的时候就初始化了一份单例对象&#xff0c;所以他不存在线程安全问题。优点&#xff1a;不存在线程安全问题&#xff0c;天然的线程安全缺点&#xff1a;在类加载的时候就已经创建了对象&#xff0c;如果后续代码里没有使用到单例&#xff0c;就…

跟20%的同行去竞争80%的蓝海市场不香吗?

近年来&#xff0c;由于科技的发展等诸多因素&#xff0c;跨境电商行业有了长足的发展空间&#xff0c;不少人也有想要入行的打算。对于不是很了解这一行业的新手来说&#xff0c;如何选择合适的跨境电商市场与平台就显得至关重要。 一直以来&#xff0c;作为全球第四大电商市…

Android自定义View实现横向的双水波纹进度条

效果图&#xff1a;网上垂直的水波纹进度条很多&#xff0c;但横向的很少&#xff0c;将垂直的水波纹改为水平的还遇到了些麻烦&#xff0c;现在完善后发布出来&#xff0c;希望遇到的人少躺点坑。思路分析整体效果可分为三个&#xff0c;绘制圆角背景和圆角矩形&#xff0c;绘…