什么是红黑树

2-3树

  • 性质:
  1. 满足二叉搜索树的基本性质
  2. 节点可以存放一个或者两个元素,存放一个元素的节点成为2节点,存放两个元素的节点成为3节点

image.png

  1. 2-3树是一棵绝对平衡的树,即从根节点到任意一个叶子节点所经过的节点数都相同
  • 下图是一棵完整的2-3树:

image.png

2-3树是如何维持绝对平衡的

向2-3树中添加节点的操作如下:

对于2-3树来说,每次添加节点永远不会添加到一个空的位置!

  • 向2-节点中插入数据6,6和12融合形成一个3节点:

image.png

  • 向3-节点中插入数据2,先融合暂时形成一个4节点,再分裂:

image.png

  • 向3-节点中插入数据4,并且该3-节点父节点是2-节点,3节点先融合暂时形成一个4节点,再分裂,接着与父节点进行融合:

image.png

  • 向3-节点中插入数据4,并且该3-节点父节点也是3-节点,该3节点先融合暂时形成一个4节点,再分裂,接着与父节点进行融合暂时形成一个4节点,这个4节点再分裂:

image.png

红黑树和2-3树

红黑树本质上其实等价于2-3树,2-3树中2-节点等同于红黑树中的“黑”节点,2-3树中的3-节点等同于红黑树中的“红”节点+红边+“黑”节点。

其中红节点表示和父节点是是一种并列的关系,由于在树的实现过程是一个二叉树,即每个节点最多只能存储一个元素,红节点的引入是为了表示2-3树的3节点的一种实现方式。在所有的红黑树中,所有的红节点一定是向左倾斜的。

image.png

基于红黑树和2-3树的关系,我们可以将2-3树转化为红黑树:

image.png

image.png

红黑树性质

  1. 每个节点或者是红色的,或者是黑色的
  2. 根节点是黑色的
  3. 每一个叶子节点(最后的空节点)是黑色的

    这其实是一个定义

  4. 如果一个节点是红色的,那么它们的孩子节点都是黑色的

    因为红节点和其父亲节点表示一个3节点,黑色节点的右孩子一定是黑色节点。

  5. 从任意一个节点到叶子节点经过的黑色节点都是一样的

    因为2-3树是绝对平衡的

image.png

相较于AVL树,红黑树上的查找的操作的效率的低于AVL树的。但对于增删操作,红黑树的性能是更优的。

添加新元素

节点的默认颜色设置为红色,因为添加新元素的时候,默认先是与一个节点进行融合的。

  1. public class RBTree<K extends Comparable<K>,V>{
  2. private static final boolean RED=true;
  3. private static final boolean BLACK=false;
  4. private class Node{
  5. public K key;
  6. public V value;
  7. public Node left,right;
  8. public boolean color;
  9. public Node(K key,V value){
  10. this.key=key;
  11. this.value=value;
  12. this.left=null;
  13. this.right=null;
  14. //2-3树中添加一个新元素
  15. //元素添加进2-节点,会形成3-节点
  16. //元素添加进3-节点,会先形成4-节点,然后进行相应的转换
  17. //因为2-3树添加新元素的时候,永远不会将其放置到空位置上
  18. //而是默认与一个节点融合,所以节点的默认颜色设置为红色
  19. this.color=RED;
  20. }
  21. }
  22. }

保持根节点为黑色

  1. //判断节点是否是红色
  2. private boolean isRed(Node node){
  3. if(node==null){
  4. return BLACK;
  5. }
  6. return node.color;
  7. }
  8. public void add(K key, V value) {
  9. root=add(root,key,value);
  10. //红黑树性质: 2.根节点是黑色的
  11. root.color=BLACK;
  12. }

左旋转

对以node为根节点的如下子树进行左旋转:

image.png

  1. 首先进行节点的左旋转

image.png

  1. 再进行颜色的翻转

image.png

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

右旋转

右旋转和左旋转同理

image.png

  1. 首先进行右旋转

image.png

  1. 再进行颜色的翻转

image.png

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

颜色翻转

2-3树中3-节点插入66:

image.png

对应的红黑树中:

image.png

此时可以将这三个节点就是2-3树中的4-节点,这时就需要颜色翻转:

image.png

注:44转换为红色,方便后续的操作,(与其他的节点“融合”)

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

添加新元素

红黑树添加新元素的过程可以概括为下面一个过程:

image.png

  1. public void add(K key, V value) {
  2. root=add(root,key,value);
  3. //红黑树性质: 2.根节点是黑色的
  4. root.color=BLACK;
  5. }
  6. private Node add(Node node,K key,V value){
  7. if(node==null){
  8. size++;
  9. return new Node(key,value);
  10. }
  11. if(key.compareTo(node.key)<0){
  12. node.left=add(node.left,key,value);
  13. }else if(key.compareTo(node.key)>0){
  14. node.right=add(node.right,key,value);
  15. }else{
  16. node.value=value;
  17. }
  18. // 黑
  19. // /
  20. // 红
  21. // \
  22. // 红
  23. //左旋转
  24. if(isRed(node.right) && !isRed(node.left)){
  25. node=leftRotate(node);
  26. }
  27. // 黑
  28. // /
  29. // 红
  30. // /
  31. // 红
  32. //右旋转
  33. if(isRed(node.left) && isRed(node.left.left)){
  34. node=rightRotate(node);
  35. }
  36. // 黑
  37. // / \
  38. // 红 红
  39. // 颜色翻转
  40. if(isRed(node.left) && isRed(node.right)){
  41. flipColors(node);
  42. }
  43. return node;
  44. }

红黑树性能总结

  • 对于完全随机的数据,建议使用普通的BST。但是在极端情况下,会退化成链表!(或者高度不平衡)
  • 对于查询较多的使用情况,建议使用AVL树。
  • 红黑树牺牲了平衡性(2log n的高度),统计性能更优(增删改查所有的操作)