深读源码-java集合之TreeMap源码分析(二)

2023-11-07 09:32

本文主要是介绍深读源码-java集合之TreeMap源码分析(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

插入元素

插入元素,如果元素在树中存在,则替换value;如果元素不存在,则插入到对应的位置,再平衡树。

public V put(K key, V value) {Entry<K,V> t = root;if (t == null) {// 如果没有根节点,直接插入到根节点compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}// key比较的结果int cmp;// 用来寻找待插入节点的父节点Entry<K,V> parent;// 根据是否有comparator使用不同的分支Comparator<? super K> cpr = comparator;if (cpr != null) {// 如果使用的是comparator方式,key值可以为null,只要在comparator.compare()中允许即可// 从根节点开始遍历寻找do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)// 如果小于0从左子树寻找t = t.left;else if (cmp > 0)// 如果大于0从右子树寻找t = t.right;else// 如果等于0,说明插入的节点已经存在了,直接更换其value值并返回旧值return t.setValue(value);} while (t != null);}else {// 如果使用的是Comparable方式,key不能为nullif (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;// 从根节点开始遍历寻找do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)// 如果小于0从左子树寻找t = t.left;else if (cmp > 0)// 如果大于0从右子树寻找t = t.right;else// 如果等于0,说明插入的节点已经存在了,直接更换其value值并返回旧值return t.setValue(value);} while (t != null);}// 如果没找到,那么新建一个节点,并插入到树中Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)// 如果小于0插入到左子节点parent.left = e;else// 如果大于0插入到右子节点parent.right = e;// 插入之后的平衡fixAfterInsertion(e);// 元素个数加1(不需要扩容)size++;// 修改次数加1modCount++;// 如果插入了新节点返回空return null;
}

插入再平衡

插入的元素默认都是红色,因为插入红色元素只违背了第4条特性,那么我们只要根据这个特性来平衡就容易多了。

根据不同的情况有以下几种处理方式:

  1. 插入的元素如果是根节点,则直接涂成黑色即可,不用平衡;

  2. 插入的元素的父节点如果为黑色,不需要平衡;

  3. 插入的元素的父节点如果为红色,则违背了特性4,需要平衡,平衡时又分成下面三种情况:

(如果父节点是祖父节点的左节点)

情况策略
1)父节点为红色,叔叔节点也为红色(1)将父节点设为黑色;
(2)将叔叔节点设为黑色;
(3)将祖父节点设为红色;
(4)将祖父节点设为新的当前节点,进入下一次循环判断;
2)父节点为红色,叔叔节点为黑色,且当前节点是其父节点的右节点(1)将父节点作为新的当前节点;
(2)以新当节点为支点进行左旋,进入情况3);
3)父节点为红色,叔叔节点为黑色,且当前节点是其父节点的左节点(1)将父节点设为黑色;
(2)将祖父节点设为红色;
(3)以祖父节点为支点进行右旋,进入下一次循环判断;

(如果父节点是祖父节点的右节点,则正好与上面反过来)

情况策略
1)父节点为红色,叔叔节点也为红色(1)将父节点设为黑色;
(2)将叔叔节点设为黑色;
(3)将祖父节点设为红色;
(4)将祖父节点设为新的当前节点,进入下一次循环判断;
2)父节点为红色,叔叔节点为黑色,且当前节点是其父节点的左节点(1)将父节点作为新的当前节点;
(2)以新当节点为支点进行右旋;
3)父节点为红色,叔叔节点为黑色,且当前节点是其父节点的右节点(1)将父节点设为黑色;
(2)将祖父节点设为红色;
(3)以祖父节点为支点进行左旋,进入下一次循环判断;

让我们来看看TreeMap中的实现:

/*** 插入再平衡*(1)每个节点或者是黑色,或者是红色。*(2)根节点是黑色。*(3)每个叶子节点(NIL)是黑色。(注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!)*(4)如果一个节点是红色的,则它的子节点必须是黑色的。*(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。*/
private void fixAfterInsertion(Entry<K,V> x) {// 插入的节点为红节点,x为当前节点x.color = RED;// 只有当插入节点不是根节点且其父节点为红色时才需要平衡(违背了特性4)while (x != null && x != root && x.parent.color == RED) {if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {// a)如果父节点是祖父节点的左节点// y为叔叔节点Entry<K,V> y = rightOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {// 情况1)如果叔叔节点为红色// (1)将父节点设为黑色setColor(parentOf(x), BLACK);// (2)将叔叔节点设为黑色setColor(y, BLACK);// (3)将祖父节点设为红色setColor(parentOf(parentOf(x)), RED);// (4)将祖父节点设为新的当前节点x = parentOf(parentOf(x));} else {// 如果叔叔节点为黑色// 情况2)如果当前节点为其父节点的右节点if (x == rightOf(parentOf(x))) {// (1)将父节点设为当前节点x = parentOf(x);// (2)以新当前节点左旋rotateLeft(x);}// 情况3)如果当前节点为其父节点的左节点(如果是情况2)则左旋之后新当前节点正好为其父节点的左节点了)// (1)将父节点设为黑色setColor(parentOf(x), BLACK);// (2)将祖父节点设为红色setColor(parentOf(parentOf(x)), RED);// (3)以祖父节点为支点进行右旋rotateRight(parentOf(parentOf(x)));}} else {// b)如果父节点是祖父节点的右节点// y是叔叔节点Entry<K,V> y = leftOf(parentOf(parentOf(x)));if (colorOf(y) == RED) {// 情况1)如果叔叔节点为红色// (1)将父节点设为黑色setColor(parentOf(x), BLACK);// (2)将叔叔节点设为黑色setColor(y, BLACK);// (3)将祖父节点设为红色setColor(parentOf(parentOf(x)), RED);// (4)将祖父节点设为新的当前节点x = parentOf(parentOf(x));} else {// 如果叔叔节点为黑色或者叔叔节点不存在// 情况2)如果当前节点为其父节点的左节点if (x == leftOf(parentOf(x))) {// (1)将父节点设为当前节点x = parentOf(x);// (2)以新当前节点右旋rotateRight(x);}// 情况3)如果当前节点为其父节点的右节点(如果是情况2)则右旋之后新当前节点正好为其父节点的右节点了)// (1)将父节点设为黑色setColor(parentOf(x), BLACK);// (2)将祖父节点设为红色setColor(parentOf(parentOf(x)), RED);// (3)以祖父节点为支点进行左旋rotateLeft(parentOf(parentOf(x)));}}}// 平衡完成后将根节点设为黑色root.color = BLACK;
}

插入元素举例

我们依次向红黑树中插入 4、2、3 三个元素,来一起看看整个红黑树平衡的过程。

三个元素都插入完成后,符合父节点是祖父节点的左节点,叔叔节点为黑色,且当前节点是其父节点的右节点,即情况2)。

1

情况2)需要做以下两步处理:

(1)将父节点作为新的当前节点;

(2)以新当节点为支点进行左旋,进入情况3);

2

情况3)需要做以下三步处理:

(1)将父节点设为黑色;

(2)将祖父节点设为红色;

(3)以祖父节点为支点进行右旋,进入下一次循环判断;

3

下一次循环不符合父节点为红色了,退出循环,插入再平衡完成。


未完待续,下一节我们一起探讨红黑树删除元素的操作。


原文链接:https://www.cnblogs.com/tong-yuan/p/10657274.html

这篇关于深读源码-java集合之TreeMap源码分析(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/362741

相关文章

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.