数据结构学习笔记之红黑树

一. 定义
  红黑树和2-3树等价的,在理解了2-3树之后,再来看红黑树会比较容易理解。理解了2-3树不但对理解红黑树有帮助,还会对理解B树有帮助。

  1. 2-3 树

  2-3树是最简单的B-树结构,其每个非叶节点都有两个或三个子女,而且所有叶都在统一层上。2-3树不是二叉树,其节点可拥有3个孩子,不过,2-3树与满二叉树相似。高为h的2-3树包含的节点数大于等于高度为h的满二叉树的节点数,即至少有2^h-1个节点。

  2-3树是由二节点和三节点组成的,它是一个绝对平衡的树,它的特殊性质使得无论对该树做什么操作(添加节点或者删除节点),他都能保持树的绝对平衡(类似于满二叉树)。

2-3树

  2-3树也是一种搜索树(左子节点的值小于当前节点,右节点的值大于当前节点),在添加节点时,除了根节点之外,新节点是永远不会添加到一个空的位置的,也就是二节点只能通过分裂形成,所在在添加节点时,它实际上做了以下两步。
  (1)找寻合适的位置,融合到2-3树中的节点(已存在)中。
  (2)如果融合之后使得当前节点变成一个四节点,则分裂为一颗高度为 2 的树,然后将该树的头结点与父节点融合,重复(2) 直到融合后的节点不是四节点。

添加节点的过程
  1. 红黑树
      红黑树是一种自平衡的二叉查找树,它具有以下五条性质:
      (1)树中的节点要么是红色要么是黑色。
      (2)根节点是黑色的。
      (3)所有的叶子都是黑色(叶子是NULL节点)。
      (4)每个红色节点的两个孩子都是黑色的(红黑树中的任意一条路径不可能存在两个相连的红色节点)。
      (5)从任意节点到每个叶子节点的所有路径都包含相同数目的黑色节点。
红黑树
  1. 红黑树和 2-3 树的联系
      红黑树和2-3树之间实际上是等价的,联系如下:
红黑树和2-3树的联系

  知道了红黑树和2-3树之间的联系之后,我们再回过头来看红黑树的五个性质:
  (1)由于2-3 树是由2节点和3节点组成,根据上图中2节点和3节点与红黑树中节点的对应关系,我们可得出第一条性质。
  (2)对于红黑树中的根节点,其有可能是2-3树中的2节点和3节点,无论哪种表示其该节点都是黑色的,可得出第二条性质。
  (3)红黑树中的定义空节点为黑色的。
  (4)同样,红节点表示3节点左侧的节点,而2-3树的左侧节点无非连接的是2节点或者3节点,所以可以得出第四条性质。
  (5)因为2-3树是一种绝对平衡的,所以对于任意一个节点,其子节点的高度是一致的,所以经过的路径长度是一致的,由上图的转化关系可知,无论是2节点还是3节点转化成红黑树时都会有一个黑节点,所以对于红黑树中的黑节点也是绝对平衡的,所以得出第五条性质。
  红黑树是保持"黑平衡"的二叉树,严格意义来说不是平衡二叉树。

二. 基本使用
  向红黑树添加的新节点一定是红色的,因为红节点表示与父节点融合,所以当添加红节点时,树还会是平衡的。红黑树(左倾)添加节点比较复杂,包括六种情况,分别如下:
  (1)红黑树为空时,添加完节点之后,要把新添加的红节点着为黑色。
  (2)当添加的节点在黑节点的左侧时,此时相当于向 2-3 树的2节点融合新节点成为3节点,直接添加即可。

二节点左侧

  (3)当新添加的节点在黑节点的右侧时,根据2-3树的性质,红节点一定是左倾斜的,所以需要进行左旋,具体流程如下:

左旋
    private Node leftRotate(Node node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        // 由于 x 节点要替换到原来 node 的位置,所以x的颜色要和node一致
        x.color = node.color;
        // 由于node节点要与 x 节点表示2-3 树的2节点, 所以描为红色
        node.color = Node.RED;
        return x;
    }

  (4)当新添加节点在2-3树3节点(红+黑)的右侧时(红+黑+红),会形成一个4节点,此时根据2-3树的性质要将该节点拆分为3个2节点,所以根据对应关系先将所有的节点着黑色,拆分成树之后根节点要和父节点融合,所以将根节点着红色。

颜色翻转
    private Node flipColors(Node node){
        node.color = Node.RED;

        node.left.color = Node.BLACK;
        node.right.color = Node.BLACK;
        return node;
    }

  (5)当新添加节点在3节点的左侧时,会形成(红+红+黑)的情况,所以将节点右旋,此时会转化成第四种情况(x节点:红黑红)。

红红黑
    private Node rightRotate(Node node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;

        // 由于 x 节点要替换到原来 node 的位置,所以x的颜色要和node一致
        x.color = node.color;
        // 由于node要与父节点表示3节点,着红色
        node.color =  Node.RED;
        return flipColors(x);
    }

  (6)"红红黑"的第二种情况,新添加的红节点是之前红节点的右节点时,应该进行如下处理。

红红黑

  以上就为红黑树添加节点的所有可能的情况,对于这六种情况可以通过以下过程来进行自平衡的操作。实际上对于当前正在构建的节点只可能包含三种情况:红节点在当前节点的右孩子、当前节点的左孩子以及左孩子的左孩子为红节点、当前节点的左右孩子为红节点,对于第六种情况实际上在子节点的构建中已经变成了第五种情况。

总体流程

  对于其他两种情况,我们无需做任何操作,只需要在构建红黑树完成之后,手动的将红黑树的根节点着为黑色即可。
  

         private Node addNode(Node root, E e){
        // 使用e 构建子树,返回重构之后的树
        if(root == null) {
            size ++;
            return new Node(e);
        }
        // 根据 e 的值和当前值作比较,决定将 e 插入到左(右)子树中
        if(root.e.compareTo(e) < 0){
            root.right = addNode(root.right, e);
        }else if(root.e.compareTo(e) > 0){
            root.left = addNode(root.left, e);
        }

        //进行左旋
        if(isRed(root.right) && !isRed(root.left)){
            root = leftRotate(root);
        }
        //进行右旋
        if(isRed(root.left) && isRed(root.left.left)){
            root = rightRotate(root);
        }
        //颜色翻转
        if(isRed(root.left) && isRed(root.right)){
            root = flipColors(root);
        }
        // 构建完成之后,将重构之后的树返回
        return root;
    }

三. 扩展
  红黑树的删除节点、右倾红黑树(本文是左倾红黑树,也就是红节点在黑节点的左侧)、伸展树、算法导论中另一种红黑树的实现。

代码地址

你可能感兴趣的:(数据结构学习笔记之红黑树)