数据结构学习,2-3树与红黑树(java语言)

1.‘2-3’树

1.1 什么是‘2-3树’

2-3树是一种绝对平衡的树,一颗2-3树种有二节点与三节点的树结构
在这里插入图片描述

1.2 ‘2-3树’如何保持绝对平衡?

对2-3树保持绝对平衡的方法,我们先看一下如何在2-3树种添加元素
在这里插入图片描述
首先我们在空树中添加一个节点,那么这个节点毫无疑问是根节点,并且是个2节点,现在,如果我们再添加一个节点进去会怎么样呢?
在这里插入图片描述
如上图,我们将7这个元素加入2-3树中,在之前的二分搜索树中,我们会将7放入根节点的右子树中,这样无法保持树的绝对平衡,在2-3树中,我们是无法向空节点添加元素的,所以元素会和2节点组合成一个3节点
在这里插入图片描述
然后我们再向其中添加元素
在这里插入图片描述
由于我们不能向空节点中添加元素,所以我们同样,元素1先与我们的根节点组合,由于此时的根节点已经是一个3节点,而2-3树中没有4节点,所以我们先组成一个临时的4节点
在这里插入图片描述
此时的根节点不符合2-3树的定义,所以我们要将此时的4节点进行拆分操作,使其成为三个2节点
在这里插入图片描述
这样就又满足了2-3树的定义,此时我们再向其中添加元素
在这里插入图片描述
11比3大,由二分搜索树的性质,要向其右子树中添加,11又比7大,又要向其右子树中添加,右子树为空,由2-3树的性质,不能向空节点添加元素,所以与7这个2节点组合成一个3节点
在这里插入图片描述
此时我们再向其添加元素
在这里插入图片描述
根据之前的操作我们应该很容易知道如何添加了
在这里插入图片描述
在这里插入图片描述
此时我们的2-3树就不平衡了,要怎么办呢?我们再做一个操作将元素11所在的节点与元素3所在的节点组合成一个3节点就可以了
在这里插入图片描述
2-3树就是不断进行这样的操作来维持它的绝对平衡性

2.红黑树

2.1红黑树的性质

  1. 红黑树的节点要么是红色要么是黑色
  2. 红黑树的根节点是黑色的
  3. 每个叶子节点(NIL空节点也算)都是黑色的
  4. 每个红色节点的左右孩子节点都是黑色的
  5. 从任意一个节点到其所有叶子节点所经过的黑色节点树是一样多的(黑平衡)

2.2红黑树与‘2-3’树的等价关系

红黑树其实就是2-3树的二分搜索树表示,其中红色的节点与其父亲节点组合形成3节点,没有红色孩子节点的节点就是2节点
在这里插入图片描述
例如这颗红黑树中,这个红色的节点与其父节点(元素11所在的节点)组成一个3节点,这颗红黑树等价于以下的2-3树
在这里插入图片描述在这里插入图片描述
通过以上的对比,我们可以很容易的发现2-3树与红黑树的等价关系

在代码之中,我们可以用一个boolean型变量color来表示红色节点与黑色节点

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

并且我们添加一个私有的辅助函数判断节点的颜色

    //判断节点node的颜色
    private boolean isRed(Node node){
        if(node == null)
            return BLACK;
        else
            return node.color;
    }

2.3左旋转

我们向一个只有一个节点的红黑树中添加一个比他大的节点
在这里插入图片描述
红黑树要满足二分搜索树的性质,于是添加操作也可以和二分搜索树相同,添加到根节点的右子树中
在这里插入图片描述
由于我们的定义,红色节点只能存在于其父节点左子树中,所以我们要进行左旋转
在这里插入图片描述
左旋转之后再将节点的颜色做出改变
在这里插入图片描述
此时就完成了一个左旋转的过程

我们设置一个私有辅助函数

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

我们注意到,颜色翻转的时候我们将x节点的颜色与node节点颜色等价,却将node节点的颜色直接改成红色,为什么不将x节点的颜色直接变成黑色呢?原因很简单,如果我们一开始的node节点的一个红色节点,也能向其中添加红节点,如果我们单纯的直接将x节点变成黑色就出错了
在这里插入图片描述
在这里插入图片描述
此时元素3所在的节点需要左旋转
在这里插入图片描述
此时x节点就不是单纯的黑色了,而是红色,此时就变成了一个3节点,要进行分解操作了

2.4flipColors(颜色翻转)

在这里插入图片描述
如果我们这样添加一个元素,红黑树会变成什么样呢?
在这里插入图片描述在这里插入图片描述
在红黑树中一个黑色节点的左右孩子都是红色节点就相当于在2-3树中的4-node,需要进行拆分操作,在红黑树中很容易就能进行拆分,只要进行flipColors就可以了
在这里插入图片描述
至于元素7所在的节点是否要与其父节点进行其他操作就看父节点的颜色,若是黑色则不需要操作,若是红色则进行右旋转

同样我们设计一个名为flipColors的辅助函数

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

2.5右旋转

在2.3的最后我们得到了一个这样的红黑树
在这里插入图片描述
这显然对应的是2-3树中的4-node,它不应该存在,所以我们需要用到右旋转将他拆分
在这里插入图片描述
旋转之后同左旋转一样,要将x节点的颜色改成原先node节点的颜色,并且将node节点改成红色
在这里插入图片描述
这样我们的右旋转就完成了

代码基本与左旋转相反

    //右旋转
    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;
    }

此时右旋转之后的红黑树中,节点7的左右孩子的颜色都是红色,因此我们还需要对他进行颜色翻转操作
在这里插入图片描述
由于节点7已经是根节点了,因此再将节点7的颜色改变即可

在这里插入图片描述

2.6左旋转、右旋转、颜色翻转的关联性

通过上面的例子我们可以看出,在左旋转之后如果原本左旋转的节点的父节点是一个红色节点,就要对其父节点的父节点进行右旋转,然后再对右旋转得到的新的子树根节点进行颜色翻转
在这里插入图片描述

2.7向红黑树中添加节点

向红黑树中添加的节点都是红色节点,先用二分搜索树添加节点的方法添加进红黑树,然后再回溯通过以上三种判断(是否需要左旋转、右旋转、颜色翻转),得到新的红黑树,再维护一下根节点,使其永远保持黑色

    // 向红黑树中添加新的元素e
    public void add(K key, V value) {
        root = add(root, key, value);
        root.color = BLACK; //最终根节点为黑色节点
    }

    // 向以node为根的红黑树中插入元素e,递归算法
    // 返回插入新节点后红黑树的根
    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;
    }

3.红黑树的复杂度分析

红黑树由于是一种黑平衡的数据结构,其平衡性甚至还没有AVL树高,对查询操作时,最大可能遍历的节点数是2logn,而AVL树是logn

4.总结

二分搜索树:
优点:再储存完全随机的数据时很好用;
缺点:极端情况下可能会退化成链表;

AVL树:
优点:对于查询较多的数据,AVL树很好用;

红黑树:
优点:统计性能更优(当进行综合增删改查所有的操作时,用红黑树比较好)
缺点:牺牲了平衡性(2logn的高度)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值