http://blog.csdn.net/tobeandnottobe/article/details/7232664

TreeMap使用红黑二叉树实现。

红黑二叉树:

a.        根节点是黑色的;

b.        红色节点的儿子节点是黑色的;

c.        任何一个节点到空节点的所有路径上必包含相同数据的黑色节点;

d.          叶子节点的子节点是黑色节点

假设一颗红黑树的黑色节点个数为R,那么这棵树的最短高度为R,最大长度为2R

所以h<2R,R的最大值为log(n+1),所以红黑树的高度<2log(n+1)

先看插入:

第一步,先找到新节点在红黑二叉排序树中的位置,并插入:

[java] view plaincopyprint?
  1. int cmp;
  2. Entry<K,V> parent;
  3. // split comparator and comparable paths
  4. Comparator<? super K> cpr = comparator;
  5. if (cpr != null) {
  6. do {
  7. parent = t;
  8. cmp = cpr.compare(key, t.key);
  9. if (cmp < 0)
  10. t = t.left;
  11. else if (cmp > 0)
  12. t = t.right;
  13. else
  14. return t.setValue(value);
  15. } while (t != null);
  16. }
  17. else {
  18. if (key == null)
  19. throw new NullPointerException();
  20. Comparable<? super K> k = (Comparable<? super K>) key;
  21. do {
  22. parent = t;
  23. cmp = k.compareTo(t.key);
  24. if (cmp < 0)
  25. t = t.left;
  26. else if (cmp > 0)
  27. t = t.right;
  28. else
  29. return t.setValue(value);
  30. } while (t != null);
  31. }
  32. Entry<K,V> e = new Entry<K,V>(key, value, parent);
  33. if (cmp < 0)
  34. parent.left = e;
  35. else
  36. parent.right = e;

上述代码无需多做分析,new TreeMap的时候可以指定具体的Comparator或者使用系统默认的Comparable接口,然后按照key从根节点开始找,直到具体可以插入的接口,插入即可;

第二步,因为二叉树中新增了一个节点,如果红黑树的平衡性受了影响,就需要进行调整:

新插入的节点先标成红色,如果该插入的节点不是根节点而且新节点的父节点是黑色的话,那么新加节点后,没有违法上述abcd四点,红黑二叉排序树就不需要再做任何调整。所以只有当新插入节点非root节点或者其对应的父节点是红色节点的时候才需要重新调整:

[java] view plaincopyprint?
  1. while (x != null && x != root && x.parent.color == RED)

如果新节点X对应的父节点是个左孩子节点的话,会进行以下调整:

取到新节点父节点的兄弟节点y,如果兄弟节点是红色,那么就将x和y标记为黑色,x父节点和其兄弟节点y对应的父节点标记为红色,此时因为他们的父节点的父节点也有可能是红色的,所以需要将父节点赋值给x,重新对x向上进行调整:

[java] view plaincopyprint?
  1. Entry<K,V> y = rightOf(parentOf(parentOf(x)));
  2. if (colorOf(y) == RED) {
  3. setColor(parentOf(x), BLACK);
  4. setColor(y, BLACK);
  5. setColor(parentOf(parentOf(x)), RED);
  6. x = parentOf(parentOf(x));
  7. }

否则如果兄弟节点y是黑色的:

如果此时x是左节点的话,就要将x父节点设置为黑色,x父节点的父节点设置为红色,然后对x父节点的父节点右旋即可,即:将x父节点的父节点极其孩子节点设置为x父节点的右子树,如果x是右节点的话,要先将x对应的父节点设置为x的左孩子,此时问题就和x是左节点是一样的了,只是此时的x已经是x的父节点而已,其代码如下

[java] view plaincopyprint?
  1. else {
  2. if (x == rightOf(parentOf(x))) {//如果x是右节点的话,要先将x对应的父节点设置为x的左孩子
  3. x = parentOf(x);
  4. rotateLeft(x);
  5. }
  6. setColor(parentOf(x), BLACK);//x父节点设置为黑色
  7. setColor(parentOf(parentOf(x)), RED);//x父节点的父节点设置为红色
  8. rotateRight(parentOf(parentOf(x)));//x父节点的父节点右旋成为x父节点的右孩子
  9. }

如果新节点x对应的父节点是右孩子节点的话,其调整规则和上述是雷同的,在此不再累述,见详细代码:

[java] view plaincopyprint?
  1. else {
  2. Entry<K,V> y = leftOf(parentOf(parentOf(x)));
  3. if (colorOf(y) == RED) {
  4. setColor(parentOf(x), BLACK);
  5. setColor(y, BLACK);
  6. setColor(parentOf(parentOf(x)), RED);
  7. x = parentOf(parentOf(x));
  8. } else {
  9. if (x == leftOf(parentOf(x))) {
  10. x = parentOf(x);
  11. rotateRight(x);
  12. }
  13. setColor(parentOf(x), BLACK);
  14. setColor(parentOf(parentOf(x)), RED);
  15. rotateLeft(parentOf(parentOf(x)));
  16. }
  17. }

右旋转的代码如下:

[java] view plaincopyprint?
  1. private void rotateRight(Entry<K,V> p) {
  2. if (p != null) {
  3. Entry<K,V> l = p.left;
  4. p.left = l.right;
  5. if (l.right != null) l.right.parent = p;
  6. l.parent = p.parent;
  7. if (p.parent == null)
  8. root = l;
  9. else if (p.parent.right == p)
  10. p.parent.right = l;
  11. else p.parent.left = l;
  12. l.right = p;
  13. p.parent = l;
  14. }
  15. }

效果就是将当前节点的设置为其子节点的右节点,并将子节点节点原来的右节点设置为当前节点的左子节点;

左旋转的效果就是将当前节点设置为右子节点的左节点,右子节点原来的左子节点设置为当前节点的右子节点,代码如下:

[java] view plaincopyprint?
  1. private void rotateLeft(Entry<K,V> p) {
  2. if (p != null) {
  3. Entry<K,V> r = p.right;
  4. p.right = r.left;
  5. if (r.left != null)
  6. r.left.parent = p;
  7. r.parent = p.parent;
  8. if (p.parent == null)
  9. root = r;
  10. else if (p.parent.left == p)
  11. p.parent.left = r;
  12. else
  13. p.parent.right = r;
  14. r.left = p;
  15. p.parent = r;
  16. }
  17. }

TreeMap删除元素:

和新增元素一样,删除元素也可能会破坏二叉树的平衡性,所以也可能需要做调整,查找过程就不说了,看看删除的时候我们如何调整来维护其平衡性的:

假如当前要被删除节点有两个孩子节点,那么只需要将其后继节点查找出来,将其内容换成后继节点的内容,然后问题就转变成删除后继节点的问题.这里是找到当前节点的右子树中最小的那个节点的内容替代之,

[java] view plaincopyprint?
  1. Entry<K,V> p = t.right;
  2. while (p.left != null)
  3. p = p.left;
  4. return p;

这样删除有两个孩子节点的节点问题就转换成删除带有一个子节点或者删除叶子节点的问题。接下来一段代码是:

[java] view plaincopyprint?
  1. // Start fixup at replacement node, if it exists.
  2. Entry<K,V> replacement = (p.left != null ? p.left : p.right);

假如这里的p是上述后继节点得来,因为此时p不可能有左孩子,那替代者就是p.right,此时可能为null,或者是被删除节点的子节点少于两个的情况,可能是p.left,也可能是p.right.如果不是null的话就要将后继节点指向到p的parent节点,用于删除当前节点:

[java] view plaincopyprint?
  1. replacement.parent = p.parent;
  2. if (p.parent == null)
  3. root = replacement;
  4. else if (p == p.parent.left)
  5. p.parent.left  = replacement;
  6. else
  7. p.parent.right = replacement;

此时p被删除,假如p是红色的,那就不会影响红黑树,但是如果被删除的节点是黑色的,此时因为删掉了一个黑色节点,所以需要对原来的树进行调整:

[java] view plaincopyprint?
  1. while (x != root && colorOf(x) == BLACK) {
  2. if (x == leftOf(parentOf(x))) {//如果x是左子节点
  3. Entry<K,V> sib = rightOf(parentOf(x));
  4. if (colorOf(sib) == RED) {//如果其兄弟节点为红色
  5. setColor(sib, BLACK);//兄弟节点设为黑色
  6. setColor(parentOf(x), RED);//父节点设置为红色
  7. rotateLeft(parentOf(x));//父节点左旋
  8. sib = rightOf(parentOf(x));
  9. }
  10. if (colorOf(leftOf(sib))  == BLACK &&
  11. colorOf(rightOf(sib)) == BLACK) {//如果兄弟节点的两个儿子都是黑色
  12. setColor(sib, RED);//兄弟节点设置为红色
  13. x = parentOf(x);
  14. } else {
  15. if (colorOf(rightOf(sib)) == BLACK) {
  16. setColor(leftOf(sib), BLACK);
  17. setColor(sib, RED);
  18. rotateRight(sib);
  19. sib = rightOf(parentOf(x));
  20. }
  21. setColor(sib, colorOf(parentOf(x)));
  22. setColor(parentOf(x), BLACK);
  23. setColor(rightOf(sib), BLACK);
  24. rotateLeft(parentOf(x));
  25. x = root;
  26. }
  27. }
  28. setColor(x, BLACK);

另外,如果replacement值是空的,就说明被删除节点没有子节点,如果是黑色节点需要进行调整再删除,如果不是就直接删除即可

[java] view plaincopyprint?
  1. if (p.color == BLACK)
  2. fixAfterDeletion(p);
  3. if (p.parent != null) {
  4. if (p == p.parent.left)
  5. p.parent.left = null;
  6. else if (p == p.parent.right)
  7. p.parent.right = null;
  8. p.parent = null;
  9. }

因为TreeMap的查找算法是很简单的,所以在此不再累述。另外,TreeMap的一个特殊用途是它可以实现数据的区间范围查询

转载于:https://www.cnblogs.com/leeeee/p/7276059.html

深入Java集合学习系列:TreeMap实现相关推荐

  1. 深入Java集合学习系列:ArrayList的实现原理

    参考文献 深入Java集合学习系列:ArrayList的实现原理 本文转自xwdreamer博客园博客,原文链接:http://www.cnblogs.com/xwdreamer/archive/20 ...

  2. 深入Java集合学习系列:LinkedHashSet的实现原理

    转载自  深入Java集合学习系列:LinkedHashSet的实现原理 1.    LinkedHashSet概述: LinkedHashSet是具有可预知迭代顺序的Set接口的哈希表和链接列表实现 ...

  3. java hashset 实现原理_深入Java集合学习系列:HashSet的实现原理

    Updated on 九月 8, 2016 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持. ...

  4. 深入Java集合学习系列:HashSet的实现原理

    引用自 http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532999.html, 作者:xwdreamer 深入Java集合学习系列:Hash ...

  5. 【Java集合学习系列】HashMap实现原理及源码分析

    HashMap特性 hashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap接口,实现了Map接口(HashTable跟HashMap很像,HashTable中的方法是线程安全 ...

  6. 深入Java集合学习系列:HashMap的实现原理

    2019独角兽企业重金招聘Python工程师标准>>> HashMap概述: HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和 ...

  7. 深入Java集合学习系列:ConcurrentHashMap之实现细节

    http://www.iteye.com/topic/344876 ConcurrentHashMap是Java 5中支持高并发.高吞吐量的线程安全HashMap实现.在这之前我对Concurrent ...

  8. 深入Java集合学习系列:Hashtable的实现原理

    第1部分 Hashtable介绍 和HashMap一样,Hashtable也是一个散列表,它存储的内容是键值对(key-value)映射.Hashtable继承于Dictionary,实现了Map.C ...

  9. Java命令学习系列(二)——Jstack

    转载自 Java命令学习系列(二)--Jstack jstack是java虚拟机自带的一种堆栈跟踪工具. 功能 jstack用于生成java虚拟机当前时刻的线程快照.线程快照是当前java虚拟机内每一 ...

最新文章

  1. Android 双屏开发 Presentation 的使用教程
  2. Linux正则表达式grep与egrep
  3. [bzoj3670][2014湖北省队互测week2]似乎在梦中见过的样子
  4. 列表导航栏实例(02)——精美电子商务网站赏析
  5. EF-InvalidOperation系列
  6. python中的迭代器,生成器,闭包,装饰器,@property
  7. python matlabplot animate 刷新_matlab动态绘图Animation
  8. SpringBoot | 第三十五章:Mybatis的集成和使用
  9. word 编辑域中的汉字_Word中根号2、根号3怎么打?
  10. 前端的常见的面试试题
  11. 与众不同 windows phone (18) - Device(设备)之加速度传感器, 数字罗盘传感器
  12. Ubuntu18.04没有WiFi怎么解决(图文详解)
  13. bzoj 3123 可持久化线段树启发式合并
  14. Cannot add task ‘wrapper‘ as a task with that name already exists.
  15. centos配置epel和remi源
  16. shadow机械手臂系统
  17. 一款 Java 开源的即时通讯 IM 聊天系统
  18. SAP R3 display Vendor, XK03 .
  19. 网页导航树的简单快速美观之实现
  20. 为什么C语言运行效率高

热门文章

  1. java 5 线程 睡眠,Java并发编程实例--5.线程睡眠
  2. 推荐系统遇上深度学习(十五)--强化学习在京东推荐中的探索
  3. 多层陶瓷电容器用处_陶瓷电容器的用途有哪几种?
  4. mysql trigger 有时 不执行_Mysql 寒假刷题TIPs
  5. filezilla 共享多个目录_filezilla设置中文,3步搞定filezilla中文设置
  6. 软考网络工程师学习笔记6-无线通信网
  7. 代码块、final关键字、包、权限修饰符的复习理解
  8. Qt从入门到放弃_0x01:建立项目
  9. stm8s 时钟库函数选择内部RC初始化
  10. springboot(十二)-分布式锁(redis)