前言 在阅读HashMap源码时,会发现在HashMap中使用了红黑树,所以需要先了解什么是红黑树,以及其原理。从而再进一步阅读HashMap中的链表到红黑树的转换,红黑树的增删节点等。
什么是红黑树?
在HashMap中是怎么应用的?
什么是红黑树?
红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为”对称二叉B树”,它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(logN)时间内完成查找、插入和删除,这里的n是树中元素的数目。
红黑树的性质 红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
节点是红色或黑色。
根是黑色。
所有叶子都是黑色(叶子是NIL节点)。
每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
红黑树操作 左旋、右旋
插入
以二叉查找树的方法增加节点
新插入节点为红色(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。)
注意:
性质1和性质3是永远保持着的。
性质4只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
性质5只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。
插入时会遇到以下五种情形:
情形1:插入第一个节点 情形2:插入新节点,父节点是黑色 情形3:插入新节点,父节点是红色,叔父节点是红色 情形4:插入新节点,父节点是红色,叔父节点是黑色或缺省,新节点是右子节点,父节点又是其父节点的左子节点 情形5:插入新节点,父节点是红色,叔父节点是黑色或缺省,新节点是左子节点,父节点又是其父节点的左子节点。
操作:插入第一个节点 违反性质2:” 根是黑色。 “ 情形:直接插入红色节点,然后进行染色为黑色
操作:插入新节点,父节点是黑色 未违反性质 情形:直接插入
操作:插入新节点,父节点是红色,叔父节点是红色 违反性质4:” 每个红色节点必须有两个黑色的子节点。 “ 情形:将祖父节点染色,祖父节点染色后再进行重新判断进行染色或旋转
操作:插入新节点,父节点是红色,叔父节点是黑色或缺省,新节点是右子节点,父节点又是其父节点的左子节点 违反性质4:” 每个红色节点必须有两个黑色的子节点。 “ 情形:进行左旋,旋转后父节点变成左子节点,新节点变成父节点,然后重新判断进行染色或旋转
操作:插入新节点,父节点是红色,叔父节点是黑色或缺省,新节点是左子节点,父节点又是其父节点的左子节点。 违反性质4:” 每个红色节点必须有两个黑色的子节点。 “ 情形:父节点染色为黑色,进行右旋,祖父节点变为右子节点,然后重新判断进行染色或旋转
HashMap 结构 1 2 3 4 5 6 7 8 static final class TreeNode <K,V> extends LinkedHashMap .Entry<K,V> { TreeNode<K,V> parent; TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; boolean red; }
三个参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static final int TREEIFY_THRESHOLD = 8 ;static final int UNTREEIFY_THRESHOLD = 6 ;static final int MIN_TREEIFY_CAPACITY = 64 ;
链表转红黑树-treeifyBin
数组(哈希表)长度到达64
当链表长度大于等于8是会将链表转换为红黑树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 final void treeifyBin (Node<K,V>[] tab, int hash) { int n, index; Node<K,V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); else if ((e = tab[index = (n - 1 ) & hash]) != null ) { TreeNode<K,V> hd = null , tl = null ; do { TreeNode<K,V> p = replacementTreeNode(e, null ); if (tl == null ) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null ); if ((tab[index] = hd) != null ) hd.treeify(tab); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 final void treeify (Node<K,V>[] tab) { TreeNode<K,V> root = null ; for (TreeNode<K,V> x = this , next; x != null ; x = next) { next = (TreeNode<K,V>)x.next; x.left = x.right = null ; if (root == null ) { x.parent = null ; x.red = false ; root = x; } else { K k = x.key; int h = x.hash; Class<?> kc = null ; for (TreeNode<K,V> p = root;;) { int dir, ph; K pk = p.key; if ((ph = p.hash) > h) dir = -1 ; else if (ph < h) dir = 1 ; else if ((kc == null && (kc = comparableClassFor(k)) == null ) || (dir = compareComparables(kc, k, pk)) == 0 ) dir = tieBreakOrder(k, pk); TreeNode<K,V> xp = p; if ((p = (dir <= 0 ) ? p.left : p.right) == null ) { x.parent = xp; if (dir <= 0 ) xp.left = x; else xp.right = x; root = balanceInsertion(root, x); break ; } } } } moveRootToFront(tab, root); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 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;;) { if ((xp = x.parent) == null ) { x.red = false ; return x; } else if (!xp.red || (xpp = xp.parent) == null ) return root; if (xp == (xppl = xpp.left)) { if ((xppr = xpp.right) != null && xppr.red) { xppr.red = false ; xp.red = false ; xpp.red = true ; x = xpp; } else { if (x == xp.right) { root = rotateLeft(root, x = xp); xpp = (xp = x.parent) == null ? null : xp.parent; } if (xp != null ) { xp.red = false ; if (xpp != null ) { xpp.red = true ; root = rotateRight(root, xpp); } } } } else { if (xppl != null && xppl.red) { xppl.red = false ; xp.red = false ; xpp.red = true ; x = xpp; } else { 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 ) { xpp.red = true ; root = rotateLeft(root, xpp); } } } } } }