数据结构--红黑树

2-3树
2-3树
  • 满足二分搜索树的基本性质
  • 节点可以存放一个元素或者两个元素
  • 每个节点可以有2个孩子(二节点) 或者3个孩子(三节点)
  • 绝对平衡的树(从根节点到任意一个叶子节点所通过的节点数量一定是相同的)
2-3树保持平衡

添加节点永远不会去空的节点,会和最后找到的叶子节点做融合。

添加12
添加2
添加4
添加4

红黑树

等价于2-3树

  • 保持黑平衡的二叉树,左右子树的黑色节点的高度差保持绝对平衡,2-3树本身是保持绝对平衡的
  • 每个节点或者是红色的,后者是黑色的
  • 根节点是黑色的
  • 每一个叶子节点(最后的空节点)是黑色的
  • 如果一个节点是红色,它的孩子节点都是黑色,黑色节点的右孩子一定是黑色的
  • 从任意一个节点出发到叶子节点,经过的黑色节点是一样的
  • 所有的红色节点向左倾斜
  • 最大高度:2logn
红黑树与2-3树
//红黑树基于二分搜索树
public class RedBlackTree , V>{

    private static final boolean RED = true;
    private static final boolean Black = false;

    private class Node{
        public K key;
        public V value;
        public Node left, right;
        public boolean color;

        public Node(K key, V value){
            this.key = key;
            this.value = value;
            left = null;
            right = null;
            color = RED;//因为在2-3树中添加的节点永远都是要融合到已有的节点中
        }
    }

    private Node root;
    private int size;

    public RedBlackTree(){
        root = null;
        size = 0;
    }

    public int getSize() {
        return size;
    }


    public boolean isEmpty() {
        return size == 0;
    }


    //判断node颜色
    private boolean isRed(Node node) {
        if (node == null)
            return Black;
        return node.color;
    }

    public boolean contains(K key) {
        return getNode(root, key) != null;
    }


    public V get(K key) {
        Node node = getNode(root, key);
        return node == null? null : node.value;
    }


    public void set(K key, V newValue) {
        Node node = getNode(root, key);
        if (node == null)
            throw new IllegalArgumentException(key + "doesn`t exist");
        node.value = newValue;
    }

}
添加元素相关操作
  • 左旋转,当添加的元素大于节点时,进行左旋转。(红色节点需要向左倾斜)
添加42

左旋转

左旋转代码

    //     node                                x
    //    /   \           左旋转             /    \
    //   T1    x      ----------->       node    T3
    //       /  \                       /   \
    //      T2  T3                     T1   T2
    //左旋转
    private Node leftRotate(Node node) {

        Node x = node.right;
        //左旋转
        node.right = x.left;
        x.left = node;

        x.color = node.color;
        node.color = RED; //是2-3树中的三节点

        return x;
    }
  • 颜色翻转,添加66,因为42需要继续向上融合(向红黑树中的3节点添加元素)
添加66
颜色翻转

颜色翻转代码

    private void flipColors(Node node) {
        node.color = RED; //由于需要继续向上融合 所以是红色 
        node.left.color = Black;  // 三个二节点 是黑色
        node.right.color = Black; // 三个二节点 是黑色
    }
  • 右旋转,需要翻转颜色
添加12
右旋转
颜色翻转

右旋转代码

    //右旋转
    //        node                           x
    //        /   \       右旋转            /   \
    //       x    T2  -------------->     y    node
    //     /  \                               /   \
    //    y   T1                             T1   T2
    private Node rightRotate(Node node) {
        Node x = node.left;

        //右旋转
        node.left = x.right;
        x.right = node;

        x.color = node.color;
        node.color = RED;

        return x;
    }
  • 先左旋转在右旋转在翻转颜色


    添加中间值元素
先左旋转
再右旋转
颜色翻转
添加元素过程总结
添加元素维护逻辑
    //向红黑树中添加元素
    public void add(K key, V value) {
        root = add(root, key, value);
        root.color = Black;  //最终的根节点为黑色节点
    }


    //以向node为根的红黑树中插入元素(key, value), 递归算法
    //返回插入新节点后红黑树的根
    private Node add(Node node, K key, V value) {
        if (node == null) {
            size ++;
            return new Node(key, value); // 默认插入红色节点
        }

        //如果相等 则不作处理
        if (key.compareTo(node.key) < 0)
            node.left = add(node.left, key, value);
        else if (key.compareTo(node.key) > 0)
            node.right = add(node.right, key, value);
        else // ==
            node.value = value;

        //右孩子是红色 左孩子不是红色的
        if (isRed(node.right) && !isRed(node.left))
            node = leftRotate(node);

        //左孩子 以及 左孩子的左孩子都是红色的
        if (isRed(node.left) && isRed(node.left.left))
            node = rightRotate(node);

        //左孩子 与 右孩子 都是红色的
        if (isRed(node.left) && isRed(node.right))
            flipColors(node);

        return node;
    }

性能总结

对于完全随机的数据,普通的二分搜索树很好用
缺点:极端情况下退化成链表(或者高度不平衡)
对于查询较多的使用情况,AVL树很好用
红黑树牺牲了平衡性(2logn的高度)
红黑树统计性能更优(综合增删改查所有的操作),平均性能优于AVL树。
数据结构中经常发生添加,删除的情况时,红黑树更合适。查询并不占优势。
如果数据近乎不会动的话,只查询,AVL树性能更好。

时间复杂度: O(logn)

你可能感兴趣的:(数据结构--红黑树)