数据结构-红黑树

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

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

echo20222022

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

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

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

打赏作者

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

抵扣说明:

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

余额充值