数据结构(十 一) ---[实现红黑树(Red Black Tree)]

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构(十 一) ---[实现红黑树(Red Black Tree)]相关的知识,希望对你有一定的参考价值。

2-3树

首先23树是满足二分搜索树的性质;
节点可以存放一个或两个元素

例如这样一棵2-3树;
2-3树是绝对的平衡树

例如,现在给出这样一个数组,请将它整理为二分搜索树,以及 2-3 树
[12,23,6,9,45,36,86,25,46]

二分搜索树–>

[12,23,6,9,45,36,86,25,46]
2-3树形成过程

最后一步,添加节点46;

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

由于节点属性的话,线条颜色不好处理;那么就就节点的红黑表示红黑树中的关系;
红色节点是左倾斜的;

比如要把下面这棵2-3树变为红黑树

走到3节点17与33这块,17分离出去,

分离出的节点17的左子树也是个3节点,同样,分离

然后将3节点66与88分离

最终,整理左右关系;得到红黑树;

红黑树

  • 红黑树的根结点为黑色;叶子节点为黑色;
  • 若一个节点为红色节点,则它的子节点都为黑色节点;
  • 由任意节点到叶子节点,经过的黑色节点个数是一样的;

来感受一下构建红黑树时添加节点的过程;

添加节点时,默认为红色节点;

首先添加节点42,更新根节点后,节点42置为黑色节点;

然后添加节点37,根据二分搜索树特性,它将被挂接到根结点42的左边;

试想一下,若先添加了节点37;然后再去添加节点42呢;
由于不符合红黑树的性质,就得需要左旋处理;并且颜色转换处理;

左旋转与颜色变换

那么来具体看看这个转换过程;
(其余节点临时添加上去,为演示左旋转效果)

颜色变换;
首先想一下在2-3树中的效果; 节点37节点42是一个融合的3节点;

颜色翻转

在添加节点37和节点42后;

oK,接着添加节点66;

在2-3树中的效果是—>

考虑到节点42可能会向上融合其他节点成为3结点,所以它得是红色的;
要是这个结点没有向上融合的话(也就是说这个结点是根结点喽),不必担心;
在写添加方法的时候已经考虑到了这个问题,将所有结点添加完成后,会将树的根节点置为黑色的;

//颜色反转处理;
    private void colorFlip(TreeNode node)
        //当前节点置为红色;(考虑到这个node节点可能向上融合;而融合节点为红色的)
        node.isRed = true;
        //子节点置为黑色;
        node.left.isRed  = false;
        node.right.isRed = false;
    

右旋转与颜色变换;

OK,刚才是添加节点66的状况;我这里要是添加节点12呢;
在节点42和节点37的基础上;

在2-3树中的效果

就得考虑右旋转了;颜色变换,以及节点的颜色翻转;
这里也同样的,用几个临时节点参与演示效果;

特殊情况处理

OK,刚才是添加节点12的状况;我这里要是添加节点40呢;
在节点42和节点37的基础上;

具体操作

实现红黑树

/**
 * @author by 小智RE0
 * @date 2021-12-19
 * 红黑树
 */
public class RedBlackTree<T extends Comparable<T>> 
    //内部类节点;
    class TreeNode 
        //节点值;
        T val;
        //左节点;
        TreeNode left;
        //右结点;
        TreeNode right;
        //是否为红色节点;true:红色节点;false:黑色节点;
        boolean isRed;

        //初始化;
        public TreeNode(T val) 
            this.val = val;
            this.left = null;
            this.right = null;
            //节点默认为红色;
            this.isRed = true;
        
    

    //头结点;
    private TreeNode root;

    //定义树结点的个数;
    private int size;

    public RedBlackTree() 
        this.root = null;
        this.size = 0;
    

    //判空;
    public boolean isEmpty() 
        return this.root == null;
    


    //辅助方法;获取节点的颜色; 注意,根据属性isRed;true红色节点;false:黑色节点;
    private boolean getColor(TreeNode node) 
        if (node == null) return false;

        //根据节点的属性获取;
        return node.isRed;
    

    //颜色反转处理;
    private void colorFlip(TreeNode node) 
        //当前节点置为红色;(考虑到这个node节点可能向上融合;而融合节点为红色的)
        node.isRed = true;
        //子节点置为黑色;
        node.left.isRed = false;
        node.right.isRed = false;
    


    //左旋处理;且变换节点颜色;
    /*
     *     Node                   x
     *    /    \\     左旋        /   \\
     *   n1     x    ==>>     Node   n3
     *         / \\           /  \\
     *        n2  n3        n1  n2
     * */
    private TreeNode rotateLeft(TreeNode node) 
        //首先取出节点x,与x的左子节点;
        TreeNode x = node.right;
        TreeNode n2 = x.left;
        x.left = node;
        node.right = n2;
        //颜色变换;
        x.isRed = getColor(node);
        node.isRed = true;

        return x;
    

    //右旋转处理,且变换节点的眼色;
    /*
     *     Node                   y
     *    /    \\     右旋        /   \\
     *   y     n3    ==>>     n1    Node
     *  / \\                        /  \\
     *n1  n2                      n2  n3
     * */
    public TreeNode rotateRight(TreeNode node) 
        //首先取出节点y,与y的右子节点;
        TreeNode y = node.left;
        TreeNode n2 = y.right;

        y.right = node;
        node.left = n2;
        //颜色变换;
        y.isRed = getColor(node);
        node.isRed = true;

        return y;
    


    //添加元素;
    public void add(T ele) 
        //判断是否存在;
        if (contains(ele) != null) 
            return;
        
        //每次将创建的根节点返回;
        root = add(root, ele);
        //添加之后,让根结点变为黑色;
        root.isRed = false;
        this.size += 1;
    

    //递归添加元素的底层;
    private TreeNode add(TreeNode node, T ele) 
        //不存在就创建;
        if (node == null) 
            return new TreeNode(ele);
        
        //添加的元素和之前的根结点进行比较;
        if (ele.compareTo(node.val) > 0) 
            node.right = add(node.right, ele);
         else 
            node.left = add(node.left, ele);
        

        //红黑树操作;
        //若右树为红色时,左旋转;
        if (!getColor(node.left) && getColor(node.right)) 
            node = rotateLeft(node);
        

        //若左树为红色,左树的左树为红色时;
        if (getColor(node.left) && getColor(node.left.left)) 
            node = rotateRight(node);
        

        //左树和右树都是红色时,进行颜色反转;
        if (getColor(node.left) && getColor(node.right)) 
            colorFlip(node);
        

        return node;
    


    //查询元素;
    public TreeNode contains(T ele) 
        if (root == null) 
            return null;
        
        return contains(root, ele);
    

    //查询元素的底层;
    private TreeNode contains(TreeNode node, T ele) 
        //递归结束点;
        if (node == null) 
            return null;
        
        //将当前根结点的值存储;
        T val = node.val;
        if (ele.compareTo(val) == 0) 
            return node;
         else if (ele.compareTo(val) > 0) 
            return contains(node.right, ele);
         else 
            return contains(node.left, ele);
        
    

以上是关于数据结构(十 一) ---[实现红黑树(Red Black Tree)]的主要内容,如果未能解决你的问题,请参考以下文章

数据结构 - 红黑树(Red Black Tree)插入详解与实现(Java)

简单聊聊红黑树(Red Black Tree)

红黑树

红黑树

红黑树

算法:红黑树