红黑树实现

1. 红黑树简介

红黑树的底层是一个二分搜索树
在这里插入图片描述
《算法导论》中红黑树的定义:

在这里插入图片描述

可以简述

  • 所有节点非红即黑;
  • 根节点为黑;
  • 最后的null节点为黑;
  • 红节点的孩子一定为黑;
  • 黑平衡。

红黑树的用处

  • 重查找,通过 key 查找到对应的 value
  • 中序遍历是顺序
  • 内存管理 “malloc 申请的内存空间” 使用 红黑树 进行存储,申请一块内存,叶子节点存一块。

2. 2-3树

红黑树与2-3树具有等价性

2-3树有下列性质:

  • 满足二分搜索树的基本性质
  • 节点可以存放一个元素或者两个元素

在这里插入图片描述
a树代表2节点,bc树代表3节点。
在这里插入图片描述
  2-3树是一棵 绝对平衡的树(根节点到任意一个叶子节点所经过的节点数量一定是相同的)。

2.1 2-3树插入新节点变换

如果插入3- 节点 ,为叶子节点

    2-3- ,代表是2-3树节点的2节点,3节点。
在这里插入图片描述
如果插入3- 节点 ,父亲节点为2- 节点

在这里插入图片描述

如果插入3-节点 ,父亲节点为3-节点

在这里插入图片描述

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

在这里插入图片描述

  • b-c在2-3树中是并列关系,用红色连线表示 。
  • 红色节点表示和父亲节点,原先在2-3树中一起表示为3-节点,也即并列关系。
  • 所有的红色节点都是左倾斜的。

2-3树 与 红黑树对应关系

在这里插入图片描述
  也可以转成这样表示

在这里插入图片描述

红黑树是保持黑平衡的二叉树,严格意义上,不是平衡二叉树, 最大高度: 2logn。 时间复杂度:O(logn)。

3. 红黑树添加新元素

  • 2-3树中添加一个新元素
  • 或者添加进2-节点,形成一个3-节点
  • 或者添加进3-节点,暂时形成一个4-节点
  • 永远添加红色节点

3.1 向红黑树中的“2-node”添加元素

若添加的元素小于根节点,直接插入在根节点的左侧
在这里插入图片描述
若添加的元素大于根节点,插入在根节点的右侧,进行一次左旋转

 37号根节点插入一个42号节点
 37        单独2-,根节点37 
      37
         \
          42
进行左旋转
     42
   /
 37

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//   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;

    return x;
}

3.2 向红黑树中的“3-node”添加元素

3.2.1 若添加大于根节点,颜色翻转

   添加节点66,形成一个临时的4-节点。红色节点的意思是:它和它父亲节点融合在一起。

在这里插入图片描述
在这里插入图片描述

中间图,类似一位数组,[37, 42, 66]是临时的4-节点,进行变化;
                           42
                         /     \
                        37     66
  • 3个二节点,代表 3 个节点都是黑色节点,若左侧没有红色的节点,它本身就代表一个单独2-节点;
  • 变成 二叉树形状后,根节点42要和它的父亲节点进行融合,融合意味着新的根节点42在红黑树中要变成红色节点 ⇒ \Rightarrow 颜色翻转

在这里插入图片描述

//颜色翻转
private void flipColors(Node node) {
    node.color = RED;
    node.left.color = BLACK;
    node.right.color = BLACK;
}

3.2.2 若添加小于根节点的左孩子,右旋转

添加节点12,形成一个临时的4-节点。
在这里插入图片描述

中间图,类似一位数组,[1237, 42],类似4-节点,进行变化, “红色节点的意思是:它和它父亲融合在一起的”
                            37
                          /     \
                        12      42
转换成上述形式,需要进行右旋转

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

            //    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;
}

3.2.3 若添加大于根节点的左孩子

添加节点40,形成一个临时的4-节点。
在这里插入图片描述
左旋转
在这里插入图片描述
再右旋转
在这里插入图片描述
再进行颜色翻转

在这里插入图片描述


在这里插入图片描述

//左旋转
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);

红黑树性能总结

红黑树的底层是一个二分搜索树。

添加操作删除操作查询操作
BST
AVLAVL树比RBTree好
RBTreeRBTree比AVL好RBTree比AVL好
  • 对于完全随机的数据,普通的二分搜索树很好用!
  • 缺点:BST极端情况退化成链表(或者高度不平衡)
  • 对于查询较多的使用情况,AVL树很好用!
  • 红黑树牺牲了平衡性(2logn的高度)
  • 红黑树统计性能更优(综合增删改查所有的操作)

完整的Java代码

public class RBTree<K extends Comparable<K>, 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;
        }
    }

    private Node root;
    private int size;

    public RBTree() {
        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;
    }


    //   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;

        return x;
    }


    //      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;
    }

    //颜色翻转
    private void flipColors(Node node) {
        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

    //向红黑树中添加新的元素(key, value)
    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;
    }


    //返回以node为根节点的二分搜索树中,key所在的节点
    private Node getNode(Node node, K key) {
        if (node == null)
            return null;

        if (key.equals(node.key))
            return node;
        else if (key.compareTo(node.key) < 0)
            return getNode(node.left, key);
        else
            return getNode(node.right, key);
    }


    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;
    }

	//红黑树删除节点操作 略
}

参考链接:liuyubobobo的github

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值