红黑树

2-3树

2-3树的节点它可以有一个元素,也可以有两个元素,它也满足二分搜索树的性质
红黑树 - 图1
我们把含有两个孩子的节点称为2节点,含有3个孩子的节点称为3节点
红黑树 - 图2
2-3树是一种绝对平衡的树,所谓绝对平衡的树指的是从根节点到任意一个叶子节点,所经过的节点是都是相同的。那么2-3树是怎么做到的呢?
红黑树 - 图3

红黑树与2-3树的等价性

由于我们一般每个节点都是表示一个数据的,2-3树有点难以实现,所以有人发明一种树叫做红黑树,它可以说是2-3树的等价,那么它树如何等价的呢?
红黑树 - 图4
上图想必很清楚的描述了等价的过程
红黑树 - 图5
现在我们来实现一下上面描述的红黑树,大部分的代码都是和二分搜索树是重合的,只是在添加时有调整,另外这里我们不牵涉到从红黑树中删除元素,因为太复杂了(其实是我不会)

  1. public class RedBlackTree<E extends Comparable<E>> {
  2. //规定红色为true 黑色为false
  3. private static final boolean RED = true;
  4. private static final boolean BLACK = false;
  5. private class Node {
  6. public E e;
  7. public Node left;
  8. public Node right;
  9. public boolean color;
  10. public Node(E e) {
  11. this.e = e;
  12. left = null;
  13. right = null;
  14. //我们在2-3树中添加节点时 永远是和别的节点融合 所以默认为红色
  15. color = RED;
  16. }
  17. @Override
  18. public String toString() {
  19. return e.toString();
  20. }
  21. }
  22. private Node root;
  23. private int size;
  24. public RedBlackTree() {
  25. root = null;
  26. size = 0;
  27. }
  28. public int size() {
  29. return size;
  30. }
  31. public boolean isEmpty() {
  32. return size == 0;
  33. }
  34. public void add(E e) {
  35. root = add(root, e);
  36. }
  37. // 判断节点node的颜色
  38. private boolean isRed(Node node){
  39. if(node == null)
  40. return BLACK;
  41. return node.color;
  42. }
  43. private Node add(Node node, E e) {
  44. if (node == null) {
  45. size++;
  46. return new Node(e);
  47. }
  48. if (e.compareTo(node.e) < 0) {
  49. node.left = add(node.left, e);
  50. } else if (e.compareTo(node.e) > 0) {
  51. node.right = add(node.right,e);
  52. }
  53. return node;
  54. }
  55. public boolean contains(E e) {
  56. return contains(root, e);
  57. }
  58. private boolean contains(Node node, E e) {
  59. if (node == null) {
  60. return false;
  61. }
  62. if (e.equals(node.e)) {
  63. return true;
  64. } else if (e.compareTo(node.e) < 0) {
  65. return contains(node.left, e);
  66. } else {
  67. return contains(node.right,e);
  68. }
  69. }
  70. }

红黑树的性质

在了解了红黑树与2-3等价以后,我们来看红黑树满足哪些性质

  • 每个节点或者是红色的,或者的是黑色的
  • 根节点是黑色的

红黑树 - 图6

  • 每一个叶子节点(最后的空节点是黑色的)
    • 因为红色节点只存在于3节点中,而所有的叶子节点都是2节点
  • 如果一个节点是红色的,那么它的所有孩子节点都是黑色的

红黑树 - 图7

  • 从任意一个节点到黑色节点,经过的黑色节点是一样的
    • 因为2-3树到所有叶子节点的距离都是一样的,而经过的节点,不管是2节点还是3节点,都包括一个黑色节点,所以经过的黑色节点是一样的

向红黑树中添加元素

因为根节点是黑色的,所以我们在添加完元素后需要将根节点变为黑色

  1. public void add(E e) {
  2. root = add(root, e);
  3. root.color = BLACK;
  4. }

在添加元素到红黑树中时,可能会破坏红黑树的规则,这时就需要红黑树进行自我调整,我们就来看一下添加过程会碰到的所有情形,以及处理方法
红黑树 - 图8

  1. // node x
  2. // / \ 左旋转 / \
  3. // T1 x ---------> node T3
  4. // / \ / \
  5. // T2 T3 T1 T2
  6. private Node leftRotate(Node node){
  7. Node x = node.right;
  8. // 左旋转
  9. node.right = x.left;
  10. x.left = node;
  11. x.color = node.color;
  12. node.color = RED;
  13. return x;
  14. }

红黑树 - 图9

  1. // 颜色翻转
  2. private void flipColors(Node node){
  3. node.color = RED;
  4. node.left.color = BLACK;
  5. node.right.color = BLACK;
  6. }

红黑树 - 图10

  1. // node x
  2. // / \ 右旋转 / \
  3. // x T2 -------> y node
  4. // / \ / \
  5. // y T1 T1 T2
  6. private Node rightRotate(Node node){
  7. Node x = node.left;
  8. // 右旋转
  9. node.left = x.right;
  10. x.right = node;
  11. x.color = node.color;
  12. node.color = RED;
  13. return x;
  14. }

红黑树 - 图11
对上面的情况进行总结
红黑树 - 图12

  1. private Node add(Node node, E e) {
  2. if (node == null) {
  3. size++;
  4. return new Node(e);
  5. }
  6. if (e.compareTo(node.e) < 0) {
  7. node.left = add(node.left, e);
  8. } else if (e.compareTo(node.e) > 0) {
  9. node.right = add(node.right,e);
  10. }
  11. if (isRed(node.right) && !isRed(node.left))
  12. node = leftRotate(node);
  13. if (isRed(node.left) && isRed(node.left.left))
  14. node = rightRotate(node);
  15. if (isRed(node.left) && isRed(node.right))
  16. flipColors(node);
  17. return node;
  18. }

完整代码

  1. public class RedBlackTree<E extends Comparable<E>> {
  2. //规定红色为true 黑色为false
  3. private static final boolean RED = true;
  4. private static final boolean BLACK = false;
  5. private class Node {
  6. public E e;
  7. public Node left;
  8. public Node right;
  9. public boolean color;
  10. public Node(E e) {
  11. this.e = e;
  12. left = null;
  13. right = null;
  14. //我们在2-3树中添加节点时 永远是和别的节点融合 所以默认为红色
  15. color = RED;
  16. }
  17. @Override
  18. public String toString() {
  19. return e.toString();
  20. }
  21. }
  22. private Node root;
  23. private int size;
  24. public RedBlackTree() {
  25. root = null;
  26. size = 0;
  27. }
  28. public int size() {
  29. return size;
  30. }
  31. public boolean isEmpty() {
  32. return size == 0;
  33. }
  34. private boolean isRed(Node node) {
  35. if (node == null) {
  36. return BLACK;
  37. }
  38. return node.color;
  39. }
  40. // node x
  41. // / \ 左旋转 / \
  42. // T1 x ---------> node T3
  43. // / \ / \
  44. // T2 T3 T1 T2
  45. private Node leftRotate(Node node) {
  46. Node x = node.right;
  47. node.right = x.left;
  48. x.left = node;
  49. x.color = node.color;
  50. node.color = RED;
  51. return x;
  52. }
  53. // node x
  54. // / \ 右旋转 / \
  55. // x T2 -------> y node
  56. // / \ / \
  57. // y T1 T1 T2
  58. private Node rightRotate(Node node) {
  59. Node x = node.left;
  60. node.left = x.right;
  61. x.right = node;
  62. x.color = node.color;
  63. node.color = RED;
  64. return x;
  65. }
  66. private void flipColors(Node node) {
  67. node.color = RED;
  68. node.left.color = BLACK;
  69. node.right.color = BLACK;
  70. }
  71. public void add(E e) {
  72. root = add(root, e);
  73. root.color = BLACK;
  74. }
  75. private Node add(Node node, E e) {
  76. if (node == null) {
  77. size++;
  78. return new Node(e);
  79. }
  80. if (e.compareTo(node.e) < 0) {
  81. node.left = add(node.left, e);
  82. } else if (e.compareTo(node.e) > 0) {
  83. node.right = add(node.right,e);
  84. }
  85. if (isRed(node.right) && !isRed(node.left))
  86. node = leftRotate(node);
  87. if (isRed(node.left) && isRed(node.left.left))
  88. node = rightRotate(node);
  89. if (isRed(node.left) && isRed(node.right))
  90. flipColors(node);
  91. return node;
  92. }
  93. public boolean contains(E e) {
  94. return contains(root, e);
  95. }
  96. private boolean contains(Node node, E e) {
  97. if (node == null) {
  98. return false;
  99. }
  100. if (e.equals(node.e)) {
  101. return true;
  102. } else if (e.compareTo(node.e) < 0) {
  103. return contains(node.left, e);
  104. } else {
  105. return contains(node.right,e);
  106. }
  107. }
  108. }