红黑树 —— 个人解读

本文深入探讨了红黑树的起源,解释了红黑树的性质及其与2-3树的关系。通过示例详细阐述了2-3树如何保持绝对平衡,以及在2-3树中添加新元素的过程。接着,介绍了红黑树添加新元素的三种方式,并展示了相应的旋转和颜色翻转操作,确保红黑树性质的维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

时隔半年多,再次解读红黑树,之前的文章对红黑树(Red Black Tree)进行了只是对红黑树的性质直接进行了说明,但并没有分析红黑树到底是怎么产生的,红和黑又是如何形成的。

在《算法导论》一书中对红黑树的性质进行了深奥解读,对于初学者来说非常不友好,对于我自己来说也只是记得性质,只知其然,而不知其所以然。

A red-black tree is a binary tree that satisfies the following red-black properties:

  1. Every node is either red or black.
  2. The root is black.
  3. Every leaf(NIL)is black.
  4. If a node is red,then both its children are black.
  5. For each node, all simple paths from the node to descendant leaves contains the same number of black.

翻译成对应的中文:

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

2-3树

满足二分搜索树的基本性质,节点可以存放一个元素或者两个元素。如下图:所谓的2-3树就是每个节点有2个或者3个孩子。2-3树是一颗绝对平衡的树。
在这里插入图片描述

2-3树如何维持绝对平衡

对于2-3树来说,添加一个节点是找到最后一个叶子节点并与之融合,如下图

在这里插入图片描述
42本是一个2节点,通过融合37后变成了一个3节点,很显然此时这个2-3树依然是一棵绝对平衡的树。如果再向这个2-3树添加12,12比37小,按道理12是添加进37的左子树去,然而37的左子树为空,要记住在2-3树中添加一个节点,这个节点永远不会去一个空的位置,只会和最后找到的叶子节点做融合。这里找到的叶子节点是一个3节点,与之融合,这样成为了4节点,但在2-3树中不存在4节点,因此将其分裂,如下图:
在这里插入图片描述
这棵树依然保持绝对平衡,在这棵树的基础上再添加一个18,18与最后找到的叶子节点也就是12做一个融合,12是一个二节点,还有空间,18与之融合。如下图:
在这里插入图片描述
此时这个2-3树依然保持绝对平衡,此时再添加一个6节点,6与最后找到的叶子节点也就是12和18这个3节点做融合,形成一个临时4节点,而2-3树显然不存在4节点,需要进行拆解。拆解后的根节点12需要和它的父节点37进行融合,而37是一个2节点,故12和37可以直接融合成一个3节点。进而原来12的孩子节点就变成这个新的3节点的左孩子节点和中间孩子节点。如下图:

在这里插入图片描述
此时这个2-3树依然保持绝对平衡,此时再添加一个节点11,找到最后一个叶子节点6(6是一个二节点)直接与之融合。如下图:
在这里插入图片描述

此时这个2-3树依然保持绝对平衡,如果再来一个新节点5,找到最后一个叶子节点6和11做融合暂时形成一个4节点,然后进行拆解,对于拆解后新的子树的根节点6相应的融合到父亲节点中去,原本父亲节点是一个3节点,此时形成一个临时4节点,如下图:
在这里插入图片描述
由于在2-3树中最多只能是3节点,所以这个4节点还要继续进行分裂,和之前一样,把这个4节点的3个元素化成3个2节点,如下图:
在这里插入图片描述
之前的4节点本是一个根节点,化成这样一个形状后,也就不需要继续向上融合父亲节点了,因为根节点已经到头了。至此2-3树的添加操作到此完成。这个2-3树依然保持绝对平衡。

红黑树

对于2-3树中添加一个新元素:

或者添加进2-节点,形成一个3-节点,或者添加进3-节点,暂时形成一个4-节点,最后永远添加红色节点。

红黑树与2-3树的等价性:对应2-3树中的2节点和3节点转换为红黑树如下图:
在这里插入图片描述

在红黑树中所有的红色节点都是左倾斜的。在如下图的2-3树中有3个3节点,每一个3节点就会相应产生一个红色节点,因此对应红黑树中有3个红色节点。红黑树依然满足二分搜索树的性质。
在这里插入图片描述

红黑树中添加新元素

红黑树添加新元素有三种方式:

1.向3节点中添加一个最大的数(c>a>b)
2.向3节点中添加一个最小的数(c<b<a)
3.向3节点中添加一个中间的数(a>c>b)

向红黑树中添加新节点有3个子过程,分别是左旋转,右旋转,颜色翻转。红黑树添加新元素三种方式过程如下图:
在这里插入图片描述
红黑树维护的时机和AVL树一样,都是先使用二分搜索树的基本逻辑,把新的节点添加进红黑树中,之后再来进行红黑树性质的维护。在维护完成以后,将维护后新的根节点返回给递归调用的上一层,从上一层的角度看是否需要维护新的节点,依此类推。
三个子过程代码实现如下:

 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;
    }
     //左旋转
    private Node leftRotate(Node node) {
        Node x = node.right;
        //左旋转
        node.right = x.right;
        x.left = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

    //右旋转
    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.left) && !isRed(node.right))
            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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

༄༊心灵骇客༣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值