TreeMap
目的:
通过对JDK源码的分析,进一步了解红黑树。
目录:
1:TreeMap介绍
2:红黑树介绍
3:红黑树插入及TreeMap插入实现
4:红黑树删除及TreeMap删除实现
1:TreeMap介绍
TreeMap和HashMap同样继承于Map接口,前者是在基于红黑树,后者是基于散列。
红黑树,是一种平衡的二叉搜索树。但是它并非是严格的平衡树,而是追求局部的平衡,从而保证树的高度在lgn和2lgn之间。
所谓平衡,实际是为了解决二叉搜索树的两种极端情况即所有结点的值本来是有序,则在建立二叉搜索树的时候,该树实际变成了一个链表,它的时间复杂度是o(n)。当通过旋转操作使之称为平衡二叉树后,树的高度是lgn,则其搜索的时间复杂度是o(lgn)。
2:红黑树介绍
红黑树:红黑树首先是一个二叉搜索树.树中每个节点的值,都大于或等于在它的左子树中的所有节点的值,并且小于或等于在它的右子树中的所有节点的值。
除此之外,红黑树还有如下性质:
1:每个结点都有颜色,不是红色就是黑色
2:根节点必须是黑色
3:所有的叶子结点都是空结点(null),颜色是黑色
4:红色结点的父结点必须是黑色
5:从任意一个结点到其子树中的叶子结点的路径中包含相同数目的黑色节点。
3:红黑树插入及TreeMap插入实现
红黑树在插入新的结点的时候,规定新结点的颜色是红色。和普通二叉搜索树一样,首先找到插入的位置,之后红黑树需要维持自己的5个性质,因此需要进行修正。
当插入结点N的父节点是黑色结点的时候,显然根本不需要任何修正,上述5个性质仍旧成立。所以只需要考虑插入结点N的父节点同样是红色的情况,一共有6种情况,实际上是3种情况,因为有3种情况是对称的。
情况1:N是其父节点的左孩子,N的父节点是红色,父节点的兄弟结点是红色。
情况2:N是其父节点的左孩子,N的父节点是红色,父节点的兄弟结点是黑色。但父节点是爷爷结点的右孩子。
情况3:N是其父节点的左孩子,N的父节点是红色,父节点的兄弟结点是黑色。父节点是爷爷结点的左孩子。
情况4、5和6只是把N换成是其父节点的右孩子。
可以看出情况1并不关心N、父节点和爷爷结点的关系。但是情况2和3是关心的。
下面给出情况1、2和3的调整办法:
情况1,调整N的父节点和N的父节点的兄弟结点(称叔叔结点)的颜色为黑色,爷爷结点的颜色为红色,并设爷爷结点为新的N。这样N相当于向上调整了两层。之后再根据N所处位置判断是哪种情况继续调整。
(G代表爷爷结点Grandfather,P代表父节点Father,N代表新结点New)
情况2:
此时,违规的结点变成了P结点,相当于新的N,实际上这就转化为了情况3.
情况3:
下面以一个具体的例子介绍:
当前该红黑树满足其5个性质,现在我们需要插入一个新得结点15.
可以看出上述满足情况1.于是我们进行了调整,之后变成了右边的情形。此时违规的是结点10.
结点10违规,检查之后发现属于情况2.此时需要右旋转。一旦实行右旋转就转化为了情况3,情况3也意味着结束。
当然,以上这些结点可能只是整个树的一部分,但是整个过程就是这样。
代码实现:
//结点Entry结构 K key; V value; Entry<K,V> left = null; Entry<K,V> right = null; Entry<K,V> parent; boolean color = BLACK; //put方法 public V put(K key, V value) { //根节点 Entry<K,V> t = root; //如果根节点为空 if (t == null) { root = new Entry<K,V>(key, value, null); size = 1; modCount++; return null; } //以下是查找插入的位置X int cmp; Entry<K,V> parent; Comparator<? super K> cpr = comparator; if (cpr != null) {//如果用户设定比较器 do { //记录最终位置X的父节点 parent = t; cmp = cpr.compare(key, t.key); //根据比较选择 if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null);//直到叶子结点 } else {//如果用户没有设定比较器 if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } //初始化需要插入的结点 Entry<K,V> e = new Entry<K,V>(key, value, parent); //设定父节点和新插入结点的关系 if (cmp < 0) parent.left = e; else parent.right = e; //修复 fixAfterInsertion(e); size++; modCount++; return null; }
实际上面除了修复函数外,和普通的二叉搜索树并无多大差别,关键在于维护红黑树的性质
private void fixAfterInsertion(Entry<K,V> x) { //新结点默认为红色 x.color = RED; //如果父节点为红色 while (x != null && x != root && x.parent.color == RED) { //如果父节点是爷爷结点的左结点 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //叔叔结点y Entry<K,V> y = rightOf(parentOf(parentOf(x))); //如果叔叔结点是红色 (情况1) if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); //x重新赋值 x = parentOf(parentOf(x)); } else {//叔叔结点是黑色的 //x是父节点的右结点 (情况2) if (x == rightOf(parentOf(x))) { x = parentOf(x); //左旋 rotateLeft(x); } //此处已经转化为(情况3) setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else {//如果父节点是爷爷结点的右结点 (对称) Entry<K,V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { if (x == leftOf(parentOf(x))) { x = parentOf(x); rotateRight(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } root.color = BLACK; }
实际上插入和插入后的修复相对比较简单,删除会比较复杂些,上篇完。
相关推荐
JDK源码剖析之红黑树TreeMap,偶然看见,传上来分享一下
"Android-jdk源码阅读"这个主题聚焦于分析Java标准库中的`TreeMap`类,这是一个基于红黑树数据结构实现的有序映射。在这个主题中,我们将探讨`TreeMap`的内部工作原理,特别是它如何利用红黑树来实现高效的插入、...
而`TreeMap`则引入了红黑树算法,保证了插入、删除和查找的时间复杂度为O(log n),提高了性能。 在类库方面,JDK1.8新增了`java.time`包,替代了之前的`java.util.Date`和`java.util.Calendar`,提供了更加直观和...
下面是JDK源码中的一段代码,用于fixAfterInsertion操作: ``` private void fixAfterInsertion(Entry, V> x) { // 新加入红黑树的默认节点就是红色 x.color = RED; // ... } ``` 这段代码主要用于处理新加入红黑...
`TreeMap`使用红黑树保持键的排序,而`LinkedHashMap`则维护插入顺序或访问顺序。 在JDK 1.8中,集合框架引入了一些优化。例如,`HashMap`在容量达到一定阈值时会进行扩容,新的容量是原容量的1.5倍,这个设计是...
在JDK 1.8中,HashMap引入了红黑树(RBTree)优化,以提高性能。首先,我们来看看HashMap的基本原理。 HashMap的核心是数组和链表的结合,它维护了一个Entry对象的数组。当向HashMap中插入键值对时,会根据键的哈希...
TreeMap使用红黑树,保证了键的排序。 总的来说,理解这些集合的底层实现对于优化代码性能和正确使用它们至关重要。通过对源码的深入分析,我们可以更好地掌握Java集合框架的工作原理,并根据实际需求选择最适合的...
- `HashMap`与`TreeMap`:基于散列和红黑树的两种哈希表实现,它们在性能和排序特性上各有优势。 3. **多线程编程**: - `Thread`类:代表程序中的执行线程,理解`run()`, `start()`, `sleep()`, `join()`等方法...
因为JDK源码的命名过于简略, 不容易被理解, 但是通过我所实现的这些集合就能知道JDK中大概是怎么实现的了, 如果笔记做得有错误, 大家可以给我提issuses =,= 注意点一 本仓库为Java集合类进行源代码的分析笔记, 同时...
在JDK 1.8中,`HashMap`引入了红黑树数据结构,当哈希冲突达到一定比例时,会自动转换为红黑树,提高了查找、插入和删除的性能。 `TreeSet`和`TreeMap`是基于红黑树实现的集合,它们保证了元素的排序性。`TreeSet`...
总之,"collectionJava源码-javacollection:学习jdk1.8采集框架源代码"为我们提供了一个宝贵的资源,深入研究这些源码,不仅能增强我们对Java集合框架的理解,也能提升我们的编程技巧和解决问题的能力。
TreeSet使用红黑树数据结构,而TreeMap则使用了自平衡二叉查找树,提供了按照自然顺序或自定义比较器的排序功能。 源码分析通常包括以下几个方面: 1. **数据结构**:了解集合类底层使用的数据结构,如数组、链表...
`TreeMap`则基于红黑树,元素按键的自然排序或自定义比较器排序。 在JDK 1.8中,引入了`Stream` API,它可以与集合类一起使用,提供一种声明式处理数据的方式。例如,`stream()`方法用于创建一个流,`filter...
通过本篇内容的学习,我们不仅了解了数据结构的基础知识和Java集合框架的核心概念,还探讨了如何结合JDK源码来深入理解这些知识点。掌握好这些内容对于提高编程技能、优化代码质量和提升软件工程能力具有重要意义。...
- **Map**:HashMap和TreeMap,分别基于哈希表和红黑树,存储键值对。 - **接口与实现**:如Iterable、Iterator、ListIterator等接口,以及AbstractList、AbstractSet等抽象类。 3. **多线程**: - **Thread类**...
5. **树结构**:TreeSet和TreeMap使用红黑树实现,提供O(log n)的时间复杂度,支持高效的插入、删除和查找操作。 6. **位运算**:在`java.util.BitSet`类中,数据以位的形式存储,适合处理大量布尔值或进行位操作。...
- 学习TreeSet、TreeMap的内部实现原理,包括红黑树数据结构。 - 掌握并发容器,如ConcurrentHashMap和CopyOnWriteArrayList。 这份资源中,"Java基础上.md"和"Java基础下.md"两个文件分别涵盖了以上知识点的详细...
Java集合框架是Java编程语言中一个非常重要的组成部分,它提供了数据结构和算法的抽象,使得开发者...在阅读《分布式Java应用:基础与实践》或其他相关资料时,结合JDK源码进行深入学习,可以进一步提升你的编程能力。
在JDK 1.8及以后的版本中,当链表长度达到一定阈值时,链表会转换为红黑树,进一步提高查找效率,从O(n)降低到O(logn)。 面试中,面试官可能会询问如何解决哈希碰撞问题。HashMap通过两种方式来处理:一是通过优秀...
2. TreeMap:线程非同步,基于红黑树数据结构,按键的自然顺序或比较器排序,插入和查找操作的时间复杂度为O(logn)。 3. LinkedHashMap:线程非同步,保留插入顺序或访问顺序,继承自HashMap,同时提供有序性。 4. ...