前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >红黑树

红黑树

作者头像
程序员波特
发布2024-01-19 09:44:14
1360
发布2024-01-19 09:44:14
举报
文章被收录于专栏:魔法书
什么是红黑树

红黑树依然是一棵二分搜索树,《算法导论》中的红黑树定义如下:

  1. 每个节点或者是红色的,或者是黑色的
  2. 根节点是黑色的
  3. 每一个叶子节点(最后的空节点)是黑色的
  4. 如果一个节点是红色的,那么他的孩子节点都是黑色的
  5. 从任意一个节点到叶子节点,经过的黑色节点是一样的

  在学习红黑树之前,我们有必要先学习一下什么是2-3树,学习2-3树不仅对于理解红黑树有帮助,对于理解B类树,也是有巨大帮助的。我们常用到的磁盘存储、文件系统、数据库等相应的数据存储都是采用的B类树这样的数据结构。

什么是2-3树

  2-3树依然满足二分搜索树的基本性质,但2-3树不是一种二叉树,2-3树有两种节点,节点可以存放一个元素或者两个元素;2-3树是一棵绝对平衡的树,2-3树对于任意一个节点的的左右子树的高度一定是相等的

2-3树的添加操作

  2-3树的添加操作将新节点添加到空的位置,若添加一个比根节点小的元素,并且根节点的左子树为空,待添加的新元素会和根节点先融合,由二节点变成三节点,当此时再添加一个元素时,会发现根节点的左子树仍然为空,就还是会先和根节点向融合,但2-3树不能有四节点,最多只能有三节点,所以需要将这个四节点分裂成有三个二节点的绝对平衡二叉树,如图:

  让我们再来看一下2-3树添加元素的过程:

  再向2-3树中添加一些元素,找出规律:

  通过如上的分析,不难知道,如果添加一个元素是添加到一个2-节点,会直接与之融合,如果是添加到一个3-节点,会暂时融合形成一个四节点,然后分裂成一个绝对平衡树。如下图所示:

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

  我们在这里定义所有的红色节点都是向左倾斜的,红色节点代表与父亲节点相融合,由于我们可以通过2-3树画出一个棵红黑树:

  由此可知,红黑树是保持“黑平衡”的二叉树,严格意义上 ,不是平衡二叉树,最大高度为2logn,并且从图中也可以看出,只有三节点左侧的元素才是红色的。红黑树和AVL树:由于红黑树的最大高度是2logn,所以在查找时,相比于AVL树会慢一些,而红黑树的添加和删除元素比AVL树更快一些,如果只是用于查询,AVL树的性能要更高一些。   向红黑树中添加一个新元素,类比于2-3树中添加一个新元素,就是或者添加进2-节点,形成3-节点;或者添加进3-节点,暂时形成一个4-节点,这样我们可以让我们的红黑树,永远添加红节点。由于我们在本文是定义的所有红色节点都是向左倾斜的,当我们新添加的红色节点在根节点的右侧时,我们需要先进行左旋转擦欧总,然后再进行染色操作,在我们左旋转的过程中并不保持红黑树的性质,如下图:

左旋转的代码实现:

代码语言:javascript
复制
    //   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-节点”添加新元素,由于添加的新节点颜色都默认是红色的,红色节点表示是去和 父亲节点融合的,当4-节点分裂成3个2-节点时,新的根节点需要和父亲节点去融合,这意味着这个新的根节点需要变成红色节点。

颜色翻转的代码实现:

代码语言:javascript
复制
    // 颜色翻转
    private void flipColors(Node node){

        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

三个2-节点的自平衡,如图:

右旋转代码实现:

代码语言:javascript
复制
    //     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;
    }

三节点的另外一种情况:

像红黑树中添加节点,就分析到这里了,下面让我们来用代码实现一个红黑树和红黑树的添加操作:

代码语言:javascript
复制
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 // key.compareTo(node.key) == 0
            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 // if(key.compareTo(node.key) > 0)
            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;
    }

    // 返回以node为根的二分搜索树的最小值所在的节点
    private Node minimum(Node node){
        if(node.left == null)
            return node;
        return minimum(node.left);
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMin(Node node){

        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size --;
            return rightNode;
        }

        node.left = removeMin(node.left);
        return node;
    }

    // 从二分搜索树中删除键为key的节点
    public V remove(K key){

        Node node = getNode(root, key);
        if(node != null){
            root = remove(root, key);
            return node.value;
        }
        return null;
    }

    private Node remove(Node node, K key){

        if( node == null )
            return null;

        if( key.compareTo(node.key) < 0 ){
            node.left = remove(node.left , key);
            return node;
        }
        else if(key.compareTo(node.key) > 0 ){
            node.right = remove(node.right, key);
            return node;
        }
        else{   // key.compareTo(node.key) == 0

            // 待删除节点左子树为空的情况
            if(node.left == null){
                Node rightNode = node.right;
                node.right = null;
                size --;
                return rightNode;
            }

            // 待删除节点右子树为空的情况
            if(node.right == null){
                Node leftNode = node.left;
                node.left = null;
                size --;
                return leftNode;
            }

            // 待删除节点左右子树均不为空的情况

            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
            // 用这个节点顶替待删除节点的位置
            Node successor = minimum(node.right);
            successor.right = removeMin(node.right);
            successor.left = node.left;

            node.left = node.right = null;

            return successor;
        }
    }
}

最后,在这里做一个红黑树的总结:   对于完全随机的数据,普通的二分搜索树就很好用,缺点:极端情况会退化成链表(或者高度布不平衡);对于查询较多的情况,AVL树很好用!红黑树牺牲了平衡性(2logn的高度),统计性能更优(综和增删改查所有的操作)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是红黑树
  • 什么是2-3树
  • 2-3树的添加操作
  • 红黑树与2-3树的等价性
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档