数据结构-红黑树

2-3树

2-3树是一种绝对平衡的树结构,与红黑树具有等价性,理解了2-3树就理解了红黑树。

特性

  • 满足二分搜索树的基本性质,即左节点 < 根节点 < 右节点;
  • 2-3树是一种绝对平衡的树;
  • 节点存放一个元素也可以存放两个元素;
    • 存放一个元素的节点叫做2节点
    • 存放两个元素的节点叫做3节点

 2-3树的一个例子

 2-3树的绝对平衡性

1.向空的2-3树中添加42

 

 2.在添加37

 3.在添加12

分裂,此时绝对平衡

4.添加18,仍然绝对平衡

5. 添加6 

分裂

 节点12向上与37融合

 此时,又进入绝对平衡状态。

6. 添加11

7. 添加5

 最左侧节点进行分裂,然后与根结节点融合

 

此时又重新达到绝对平衡。

总结:

  1. 向一个2节点添加数据时,直接融合
  2. 向一个3节点添加数据时,先暂时形成4节点,在分裂成3个2节点,在把分裂后的新根与父节点融合
    1. 如果父亲节点时一个2节点,则不用继续处理
    2. 如果父节点时一个3节点,则融合后会产生一个4节点,需要继续向上分裂

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

由于在红黑树中每一个节点只能存储一个元素,所以使用一个黑节点表示2-3树中的2节点,用一个红节点+一个黑节点表示一个3节点(红节点在左侧),比如:

 

 注:红黑树中的所有节点都是向左倾斜的。

转换成红黑树后如下:

红黑树

红黑树的性质 

  • 每个节点要么是红色,用么是黑色;
  • 根节点时黑色的;
  • 每一个叶子节点(最后的空节点)都是黑色;
  • 如果一个节点时红色的,那么它的孩子都是黑色的;
  • 从任意一个节点到叶子节点,经过的黑色节点数量都是一样的;

注:

  1. 红色节点表示它与它的父节点一起组合成一个2-3树中的3节点
  2. 红黑树能够保证绝对的黑平衡

红黑树的时间复杂度

添加:O(logn)

修改:O(logn)

删除:O(logn)

添加数据

当向一个2节点中添加一个比原节点小的值是,不用做任何调整,对应在红黑树中就是如下这种情况:

左旋转

如果是在原节点的右侧添加新的节点,那情况就不一样了

由于红黑树中红节点要向左倾斜,上图结构违反了红黑树的定义,需要进行左旋转,那么推广到更一般的情况,如下:

左旋转过程如下:

node.right=x.left;
x.left = node;
x.color=node.color;
node.color=RED;

调整后的结构如下:

右旋转

如果添加节点后,形成了下面的一个红黑结构的树,就需要进行右旋转

 其中,节点12是新加的,由旋转过程如下:

node.left = x.right;
x.right = node;
x.color=node.color;
node.color = RED;

旋转后变成如下的结构:

颜色翻转

如果添加节点后形成的结构如下所示,就只需要进行颜色的翻转即可:

颜色翻转过程:

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

  

代码实现

向红黑树中添加节点时的情况可以总结为以下几种情况(删除节点时,节点结构的调整逻辑与添加时是一致的):

 

/**
 * 红黑树.
 * */
public class RBTree<E extends Comparable<E>> {

    private Node<E> root;
    private int size;

    public RBTree() {
        this.root = null;
        this.size = 0;
    }
    
    
    public void add(E e) {
        //添加操作完成后,root节点可能发生变化
        root = add(e, root);
    }

    //利用递归,添加节点
    private Node<E> add(E e, Node<E> node) {
        if (node == null) {
            size ++;
            return new Node(e);
        }

        //如果当前数据比当前节点的值小,则向左添加
        if(e.compareTo(node.data) < 0) {
            node.left = add(e, node.left);
        } else if (e.compareTo(node.data) > 0) {
            node.right = add(e, node.right);
        }

        if (isRead(node.right) && !isRead(node.left)) {
            node = leftRotate(node);
        }

        if (isRead(node.left) && isRead(node.left.left)) {
            node = rightRotate(node);
        }

        if (isRead(node.left) && isRead(node.right)) {
            flipColors(node);
        }

        return node;
    }

    //左旋转
    private Node<E> leftRotate(Node<E> node) {
        Node<E> x = node.right;
        node.right = x.left;
        x.left = node;
        x.color = node.color;
        node.color = Node.RED;

        return x;
    }

    //右旋转
    private Node<E> rightRotate(Node<E> node) {
        Node<E> x = node.left;
        node.left = x.right;
        x.right = node;
        x.color = node.color;
        node.color = Node.RED;
        return x;
    }

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

    //判断一个节点是否为红节点
    private boolean isRead(Node<E> node) {
        if (node == null) {
            return Node.BLACK;
        }
        return node.color;
    }

    public int getSize() {
        return size;
    }

    //定义节点
    private static class Node<E extends Comparable<E>> {

        private E data;
        private Node<E> left;
        private Node<E> right;

        //当前节点的颜色,true=红色,false=黑色
        public boolean color;

        //定义两个枚举值
        public static final boolean RED = true;
        public static final boolean BLACK = false;

        public Node(E data) {
            this(data,null,null);
        }

        public Node(E data, Node<E> left, Node<E> right) {
            this.data = data;
            this.right = right;
            this.left = left;
        }
    }
}

 

 

 

 

### Java红黑树数据结构实现与应用 #### 红黑树简介 红黑树是一种自平衡二叉查找树,在计算机科学中用于高效地存储和检索键值对。该数据结构的特点在于其能够自动调整节点位置来维持一定的平衡条件,从而保证最坏情况下的时间复杂度为 O(log n)[^1]。 #### 平衡特性对比 相较于 AVL 树而言,尽管两者都致力于维护二叉查找树的高度接近于最小可能高度,但是由于红黑树对于旋转次数的要求较低,因此在频繁发生插入或删除的情况下表现更优;而像 `TreeMap` 和 `TreeSet` 这样的集合类正是利用这一点实现了高效的增删改查功能[^2]。 #### 关键属性定义 为了满足上述提到的性质,每棵红黑树中的结点除了拥有指向左右子树以及父辈指针外还需要额外记录颜色信息(红色/黑色),具体如下所示: ```java private static final boolean RED = false; private static final boolean BLACK = true; static class Node<K,V> { K key; // 存储的关键字 V value; // 对应关键字所映射的对象 Node<K,V> left; // 左孩子引用 Node<K,V> right; // 右孩子引用 Node<K,V> parent;// 父亲引用 boolean color; // 颜色标记 public Node(K key, V value, Node<K,V> parent){ this.key=key; this.value=value; this.parent=parent; this.color=RED; // 新加入的节点默认设为红色 } } ``` #### 插入操作逻辑 当向一棵已有的红黑树内添加新元素时,会先按照普通的 BST 方式找到合适的位置并创建新的叶子节点。之后再依据一系列规则判断是否需要执行重涂色或是旋转变换以恢复整棵树应有的形态特征。 - 如果当前新增加的是根,则直接将其变为黑色; - 若祖父存在且叔叔也为红色,则将父亲、叔叔均改为黑色并将祖父变更为红色继续向上处理; - 当仅有单侧兄弟呈红色状态时可通过一次左(右)转使得双亲成为临时性的“假”根以便后续统一调整; - 剩余情形下只需简单交换父子间染色即可完成修复工作。 以上过程可以概括成一段伪代码形式表示出来: ```pseudo function insertFixup(node) if node is root then set its color to black and return. while (node != null && node's parent is red) // 处理违反红黑树特性的几种场景... end while ``` 实际上完整的算法较为复杂,涉及到多个分支路径的选择问题,这里仅给出大致思路供理解参考之用。 #### 删除操作概述 移除某个指定项的过程同样遵循着相似的原则——即先依照普通二叉搜索树的方式定位目标对象所在位置,并考虑三种不同类型的状况分别采取相应的措施加以应对:如果待消除的目标恰好处于末端处那么可以直接摘掉它而不影响其他部分;反之则需寻找合适的替代品填补空缺后再做进一步讨论。 值得注意的是在整个过程中可能会引入一些暂时不符合标准的情况,这就要求我们及时作出必要的修正动作直至最终达到稳定为止。 #### 应用实例分析 正如前面提及的一样,红黑树广泛应用于各种编程语言的标准库当中作为内部机制支撑起诸如有序表之类的抽象容器类型。例如在 JDK8 版本以后版本里头,`HashMap` 的链地址法解决哈希冲突方案便采用了基于红黑树优化后的链表结构,以此提高极端情况下访问效率的同时兼顾了空间利用率方面的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

echo20222022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值