[关闭]
@huangyichun 2017-08-30T15:53:46.000000Z 字数 2842 阅读 1415

红黑树

数据结构


阅读本文需要了解2-3树(实际上是一棵3阶B树),对于红黑树理解很有帮助
源码地址:https://github.com/huangyichun/DataStructure/tree/master/RB_Tree/src/main/java

定义:

满足这样定义的红黑树和相应的2-3树是一一对应的。(2-3树是一颗3阶的B树,一个结点最多有2个Key,将左边的一个变成红黑树中的红色节点,就转换成红黑树了)
image.png-81.8kB

image.png-98kB

    因为每个节点都会只有一条指向自己的链接(从它的父节点指向它),我们将链接的颜色保存在表示节点的Node数据类型的布尔变量color中。如果指向它的链接是红色的,那么该变量为true,黑色则为false。我们约定空连接为黑色。为了代码的清晰我么定义了两个常亮RED和BLACK来设置和测试这个变量。我们使用私有方法isRead()来测试一个节点和它的父节点之间的链接的颜色。

  1. /**
  2. * 红黑树
  3. */
  4. public class RBTree<Key extends Comparable<Key>, Value> {
  5. private static final boolean RED = true;
  6. private static final boolean BLACK = false;
  7. private class Node{
  8. Key key; //键
  9. Value val; //相关的值
  10. Node left ,right; //左右子树
  11. int N; //这棵子树的结点总数
  12. boolean color; //由其父节点指向它的链接的颜色
  13. public Node(Key key, Value val, int n, boolean color) {
  14. this.key = key;
  15. this.val = val;
  16. N = n;
  17. this.color = color;
  18. }
  19. }
  20. }

红黑树旋转

在操作中可能会出现红色右链接或者两条连续的红链接,我们可以通过旋转修复。
左旋转: 将一条红色的右链接转换为左链接

  1. /**
  2. * 左旋转
  3. * @param h
  4. * @return
  5. */
  6. private Node rotateLeft(Node h){
  7. Node x = h.right;
  8. h.right = x.left;
  9. x.left = h;
  10. x.color = h.color;
  11. h.color = RED;
  12. x.N = h.N;
  13. h.N = size(h.left) + size(h.right) + 1;//修改节点个数
  14. return x;
  15. }

右旋转: 将一条红色的左链接转换为右链接

  1. /**
  2. * 右旋转
  3. * @param h
  4. * @return
  5. */
  6. private Node rotateRight(Node h){
  7. Node x = h.left;
  8. h.left = x.right;
  9. x.right = h;
  10. x.color = h.color;
  11. h.color = RED;
  12. x.N = h.N;
  13. h.N = size(h.left) + size(h.right) + 1;//修改节点个数
  14. return x;
  15. }

插入:



1. 新键小于树中的两个键

image.png-29.7kB
先进行右旋转:
image.png-25.9kB
再将两个节点变为黑色:
image.png-26.1kB


2. 新键在两者之间

image.png-26.3kB
先进行左旋转:
image.png-27kB

再根据上面的情况进行处理。

3. 新键大于树中的两个键

这种情况下:
image.png-26.6kB
将两个节点变成黑色就可以了
image.png-26.1kB

当我们将插入的结点变成3这种情况时,我们需要一个函数将一个节点的两个红色子节点的颜色变为黑色。父节点变为红色。

  1. /**
  2. * 颜色转换
  3. * @param h
  4. */
  5. private void flipColors(Node h){
  6. h.color = RED;
  7. h.left.color = BLACK;
  8. h.right.color = BLACK;
  9. }

红黑色的插入所有情况上面已经介绍了,总体是将红链接在树中向上传递。

在沿着插入点到根节点的路径向上移动时在所经过的每个结点中操作顺序如下:

  • 如果右子节点是红色的而左子节点是黑色的进行左旋转;
  • 如果左子节点是红色的且它的左子节点也是红色的,进行右旋转;
  • 如果左右子节点都是红色,进行颜色转换。

  1. /**
  2. * 添加结点,如果存在修改,如果不存在创建新节点
  3. * @param h
  4. * @param key
  5. * @param val
  6. * @return
  7. */
  8. private Node put(Node h, Key key, Value val){
  9. if(h == null)//和父节点用红链接相连
  10. return new Node(key, val, 1, RED);
  11. int cmp = key.compareTo(h.key);
  12. if(cmp < 0) h.left = put(h.left, key, val);
  13. else if(cmp >0) h.right = put(h.right, key, val);
  14. else h.val = val;
  15. if(isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);//右节点为红色,左节点为黑色
  16. if(isRed(h.left) && isRed(h.left.left)) h= rotateRight(h);//左节点是红色,且它的左子节点也为红色
  17. if(isRed(h.left) && isRed(h.right)) flipColors(h);//左右子节点均为红色
  18. h.N = size(h.left) + size(h.right) + 1;//修改节点个数
  19. return h;
  20. }

注意:根结点只能是黑色,每当根结点由红色变为黑色时黑链接高度就变为1。

测试:

  1. public class Test {
  2. public static void main(String[] args) {
  3. RBTree<Integer,Character> rbTree = new RBTree();
  4. rbTree.put(18,'A');
  5. rbTree.put(14, 'B');
  6. rbTree.put(22, 'C');
  7. rbTree.put(10, 'D');
  8. rbTree.put(15, 'E');
  9. rbTree.put(19, 'F');
  10. rbTree.put(25, 'G');
  11. rbTree.put(8, 'H');
  12. rbTree.put(11, 'I');
  13. rbTree.put(23, 'J');
  14. rbTree.put(7, 'K');
  15. rbTree.printfBST();
  16. }
  17. }

输出结果:

7 8 10 11 14 15 18 19 22 23 25 

删除操作比较复杂这里就不进行介绍了。
可以查看这篇博客
https://riteme.github.io/blog/2016-3-12/2-3-tree-and-red-black-tree.html

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注