TreeMap

前言:

TreeMap和HashMap一样实现的是Map接口,但两者的实现方式天差地别。HashMap的底层是hash表+单向链表的形式存储数据,TreeMap底层是通过红黑树存储数据。HashMap因为是基于散列表的实现,所以时间开销为O(1),TreeMap的时间开销是O(lgn)。TreeMap的优势在于他是基于key值排序的。

红黑树有五大特性:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

他的所有操作都是围绕着这五大特性展开的。这五大特性的最终目的就是为了维持二叉树的相对平衡性。当每次二叉树操作以后,有可能会出现违反特性的情况(也就是出现了失衡状况),这时二叉树需要通过左旋,右旋,重新着色等系列操作,再次找到平衡点。

我的理解是:红黑色的操作就两个要点。第一:遵循二叉查找树的规范,对所有元素进行排序,但这里存在着不确定情况,有可能出现左右子树深度极其不对称的情况,导致最坏时间复杂度出现O(n)的情况。 第二:正因为存在二叉树严重不平衡的情况,所以就出现了红黑二叉树,通过标记每个节点的颜色,动态的调整二叉树的结构,使其始终维持在相对平衡的状态,这样做的好处就是查找性能始终维持在O(lgn)的较高水平。

一、添加元素

在TreeMap中插入元素,原理就是在二叉查找树中插入元素。通过对二叉树节点大小的对比插入相应的位置,时间复杂度为O(lgn)。但是在插入完毕以后,有可能会导致红黑树被破坏的情况,因此需要修复当前结构,重新着色。

put方法:

  1. public V put(K key, V value) {
  2. Entry<K,V> t = root;
  3. if (t == null) {
  4. compare(key, key); // type (and possibly null) check
  5. root = new Entry<>(key, value, null);
  6. size = 1;
  7. modCount++;
  8. return null;
  9. }
  10. int cmp;
  11. Entry<K,V> parent;
  12. // split comparator and comparable paths
  13. Comparator<? super K> cpr = comparator;
  14. if (cpr != null) {
  15. do {
  16. parent = t;
  17. cmp = cpr.compare(key, t.key);
  18. if (cmp < 0)
  19. t = t.left;
  20. else if (cmp > 0)
  21. t = t.right;
  22. else
  23. return t.setValue(value);
  24. } while (t != null);
  25. }
  26. else {
  27. if (key == null)
  28. throw new NullPointerException();
  29. @SuppressWarnings("unchecked")
  30. Comparable<? super K> k = (Comparable<? super K>) key;
  31. do {
  32. parent = t;
  33. cmp = k.compareTo(t.key);
  34. if (cmp < 0)
  35. t = t.left;
  36. else if (cmp > 0)
  37. t = t.right;
  38. else
  39. return t.setValue(value);
  40. } while (t != null);
  41. }
  42. Entry<K,V> e = new Entry<>(key, value, parent);
  43. if (cmp < 0)
  44. parent.left = e;
  45. else
  46. parent.right = e;
  47. fixAfterInsertion(e);
  48. size++;
  49. modCount++;
  50. return null;
  51. }

插入元素后调整红黑树结构:

  1. private void fixAfterInsertion(Entry<K,V> x) {
  2. x.color = RED;
  3. while (x != null && x != root && x.parent.color == RED) {
  4. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  5. Entry<K,V> y = rightOf(parentOf(parentOf(x)));
  6. if (colorOf(y) == RED) {
  7. setColor(parentOf(x), BLACK);
  8. setColor(y, BLACK);
  9. setColor(parentOf(parentOf(x)), RED);
  10. x = parentOf(parentOf(x));
  11. } else {
  12. if (x == rightOf(parentOf(x))) {
  13. x = parentOf(x);
  14. rotateLeft(x);
  15. }
  16. setColor(parentOf(x), BLACK);
  17. setColor(parentOf(parentOf(x)), RED);
  18. rotateRight(parentOf(parentOf(x)));
  19. }
  20. } else {
  21. Entry<K,V> y = leftOf(parentOf(parentOf(x)));
  22. if (colorOf(y) == RED) {
  23. setColor(parentOf(x), BLACK);
  24. setColor(y, BLACK);
  25. setColor(parentOf(parentOf(x)), RED);
  26. x = parentOf(parentOf(x));
  27. } else {
  28. if (x == leftOf(parentOf(x))) {
  29. x = parentOf(x);
  30. rotateRight(x);
  31. }
  32. setColor(parentOf(x), BLACK);
  33. setColor(parentOf(parentOf(x)), RED);
  34. rotateLeft(parentOf(parentOf(x)));
  35. }
  36. }
  37. }
  38. root.color = BLACK;
  39. }