图解红黑树

预先了解的知识

  • 二叉树

什么是红黑树

红黑树解决的问题

  在二叉树中,最坏的情况下,插入的数据类似一个链表。查找数据时就失去了二叉树的优势。平衡树就是用来解决这种情况的。红黑树就是平衡查找树的一种。
举个例子:
  假设我们要插入字母A、B、C,按照顺序插入我们得到的二叉树是:
在这里插入图片描述
我们理想中的是要得到这样的:
在这里插入图片描述
那么我们要怎样做才能得到理想中的二叉树呢?方法之一就是红黑树。先简单的介绍一下红黑树A、B、C插入顺序的过程及发生的变化:
在这里插入图片描述
从上图可以看出,红黑树插入“A”,然后插入红色的“B”,然后对“A-B”进行了旋转。然后插入“C”得到了我们想要的平衡二叉树。这就是简单的红黑树的形成。

红黑树的定义

在此我们定义红黑树与《算法导论》中略有不同,不过也是等价的:

  • 红链接均为左链接;
  • 没有任何一个结点同时和两条红链接相连;
  • 该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

该定义出自《算法》。

红链接:链接红色节点的链接。
黑高:黑色节点的高度。
我们的图片都大部分都省略了叶子结点的左右链接。空链接在这里我们定义成黑链接,也就是说叶子结点的左右链接都连接了黑节点。

旋转与颜色转换

旋转

红黑树插入的新节点都是红色节点(根节点除外)。那么就会出现右节点为红色,或者连续两个左节点为红色的情况,而旋转就是为了处理这种情况:
在这里插入图片描述

左旋转,右旋转

在这里插入图片描述

颜色转换

在插入的过程中还会出现,黑节点的左右节点都是红节点的情况,这就要通过颜色转换来处理:
在这里插入图片描述颜色转换将,左右子树的红色纷纷提到父节点,将父节点变为红色,如果父节点是根节点,则父节点颜色要变成黑色,红黑树黑高加1 。

插入

为了方便表述这里增加几个定义:
2-节点:就是普通的黑色节点,因为黑色节点必然右左右两个节点(可能都为null)。
3-节点:一个黑色节点连接一个红色节点被称为3-节点。因为红黑树的高是黑高,所以红节点我们可以想象成跟黑节点在一起的,但是这个节点有三个子节点,如图:
在这里插入图片描述插入的过程分为几种情况:

  • 对空树进行插入,即插入根节点
  • 对2-节点插入
    • 插入左节点
    • 插入右节点
  • 对3-节点插入
    • 插入左节点
    • 插入中间节点
    • 插入右节点

对空节点进行插入是最简单的,直接插入就可以了。

对2-节点插入

对2-节点进行插入也比较简单,如果插入的是左节点,则不用进行任何调整;如果插入的是右节点,则进行左旋转就可以。

对3-节点插入

插入右节点

如图插入“F”,这种情况直接进行颜色转换就可以:
在这里插入图片描述

插入左节点

如图插入“B”,对"E"节点进行右旋转得到,再进行颜色转换就可以了:
在这里插入图片描述

插入中间节点

如图插入“D”,对"C"进行左旋转,然后对“E"进行右旋转,再进行颜色转换就可以了:
在这里插入图片描述

删除

在这里对3-节点进行拓展,既然有3-节点,那么自然就有4-节点、5-节点等。

删除最小节点

删除最小节点我们可以分为以下情况:

  • 删除的是2-节点
  • 删除的是3-节点

删除的是3-节点

删除3-节点分为两种情况:如果删除的是3-节点中的红色节点,那么直接删除就可以了。如果删除的是3-节点中的黑色节点,那么需要把黑色节点通过旋转变成红色节点再删除。

删除的是2-节点

凡是涉及到删除2-节点,都会涉及到红黑树的黑平衡。因此我们在从跟节点查找最小节点的过程中,可以从别地3-节点中借一个红色节点,并将红色节点顺移下去,使最小节点形成3-节点,这样就可以轻易删除。
我们可以把红黑树分为下面几种情况:

  1. 向左查找的过程中,左边没有3-节点,右边也没有3-节点。
  2. 向左查找的过程中,左边没有3-节点,右边有3-节点。
  3. 向左查找的过程中,左边有3-节点。
    第一种情况我们要从父节点借,所以这就得保证我们的父节点一定是红色节点,所以从根节点开始,如果根节点的左右节点都不是3-节点,则直接将根节点置为红色,然后进行颜色转化。颜色转化之后树的高度减1,且树的平衡性得到保留,形成临时的4-节点:
    在这里插入图片描述第二种情况,要从右边的3-节点,“借”红色节点,使当前节点形成非2-节点,我们以跟节点为例(其余节点也是相同的操作):
    在这里插入图片描述
    第三种情况,树直接向下遍历就可以。通过反复的处理,就可以将最小的节点处理成红色节点。然后直接删除。删除之后还要向上进行遍历,将原来为了删除最小节点儿设置的不合理之处,重新恢复合理即可。

删除随机节点

删除随机节点,我们可以像删除最小节点一样一直向下遍历,直到找到要删除的节点。然后我们把要删除的节点的右子树中的最小节点赋值给我们要删除的节点,这样树中就没有了我们要删除的节点。但是却有两个一样数据的节点。因此我们将删除随机节点,成功的转化成了删除它的右子树中的最小节点。

实现

public class RedBlackBST<Key extends Comparable<Key>, Value> {

    private static final boolean RED   = true;
    private static final boolean BLACK = false;

    private Node root;     // 红黑树跟节点

    // 红黑树节点定义
    private class Node {
        private Key key;           // key
        private Value val;         // value
        private Node left, right;  // 左子树,右子树
        private boolean color;     // 节点颜色
        private int size;          // 节点数量

        public Node(Key key, Value val, boolean color, int size) {
            this.key = key;
            this.val = val;
            this.color = color;
            this.size = size;
        }
    }



    // 右旋转
    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = x.right.color;
        x.right.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }

    // 左旋转
    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = x.left.color;
        x.left.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }

    // 颜色转换
    private void flipColors(Node h) {
        h.color = !h.color;
        h.left.color = !h.left.color;
        h.right.color = !h.right.color;
    }


    /**
     * 插入方法
     * @param key the key
     * @param val the value
     * @throws IllegalArgumentException
     */
    public void put(Key key, Value val) {
        if (key == null) throw new IllegalArgumentException("first argument to put() is null");
        if (val == null) {
            delete(key);
            return;
        }

        root = put(root, key, val);
        root.color = BLACK;
        // assert check();
    }

    // 插入方法
    private Node put(Node h, Key key, Value val) {
        if (h == null) return new Node(key, val, RED, 1);

        int cmp = key.compareTo(h.key);
        if      (cmp < 0) h.left  = put(h.left,  key, val);
        else if (cmp > 0) h.right = put(h.right, key, val);
        else              h.val   = val;

        // 插入完成后要将树进行转化
        if (isRed(h.right) && !isRed(h.left))      h = rotateLeft(h);
        if (isRed(h.left)  &&  isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left)  &&  isRed(h.right))     flipColors(h);
        h.size = size(h.left) + size(h.right) + 1;

        return h;
    }


    /**
     * 删除最小节点
     * @throws NoSuchElementException
     */
    public void deleteMin() {
        if (isEmpty()) throw new NoSuchElementException("BST underflow");

        if (!isRed(root.left) && !isRed(root.right))
            root.color = RED;

        root = deleteMin(root);
        if (!isEmpty()) root.color = BLACK;
    }



    // 删除最小节点
    private Node deleteMin(Node h) {
        if (h.left == null)
            return null;

        if (!isRed(h.left) && !isRed(h.left.left))
            h = moveRedLeft(h);

        h.left = deleteMin(h.left);
        return balance(h);
    }


    /**
     * 删除随机节点
     *
     * @param  key
     * @throws IllegalArgumentException
     */
    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to delete() is null");
        if (!contains(key)) return;

        if (!isRed(root.left) && !isRed(root.right))
            root.color = RED;

        root = delete(root, key);
        if (!isEmpty()) root.color = BLACK;
    }

    // 删除随机节点
    private Node delete(Node h, Key key) {

        if (key.compareTo(h.key) < 0)  {
            if (!isRed(h.left) && !isRed(h.left.left))
                h = moveRedLeft(h);
            h.left = delete(h.left, key);
        }
        else {
            if (isRed(h.left))
                h = rotateRight(h);
            if (key.compareTo(h.key) == 0 && (h.right == null))
                return null;
            if (!isRed(h.right) && !isRed(h.right.left))
                h = moveRedRight(h);
            if (key.compareTo(h.key) == 0) {
                Node x = min(h.right);
                h.key = x.key;
                h.val = x.val;
                h.right = deleteMin(h.right);
            }
            else h.right = delete(h.right, key);
        }
        return balance(h);
    }

    // 恢复平衡
    private Node balance(Node h) {

        if (isRed(h.right))                      h = rotateLeft(h);
        if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left) && isRed(h.right))     flipColors(h);

        h.size = size(h.left) + size(h.right) + 1;
        return h;
    }

    //检查该节点是否是红色节点
    private boolean isRed(Node x) {
        if (x == null) return false;
        return x.color == RED;
    }

    //子节点数量
    private int size(Node x) {
        if (x == null) return 0;
        return x.size;
    }

    /**
     * 检查该树是否为null
     * @return
     */
    public boolean isEmpty() {
        return root == null;
    }


    /**
     * 是否存在
     * @param key
     * @return
     * @throws IllegalArgumentException
     */
    public boolean contains(Key key) {
        return get(key) != null;
    }

    /**
     * 获取key对应的value
     * @param key
     * @return
     * @throws IllegalArgumentException
     */
    public Value get(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to get() is null");
        return get(root, key);
    }

    // 获取key对应的value
    private Value get(Node x, Key key) {
        while (x != null) {
            int cmp = key.compareTo(x.key);
            if      (cmp < 0) x = x.left;
            else if (cmp > 0) x = x.right;
            else              return x.val;
        }
        return null;
    }

    // 从右边"借"红色节点
    private Node moveRedLeft(Node h) {
        flipColors(h);
        if (isRed(h.right.left)) {
            h.right = rotateRight(h.right);
            h = rotateLeft(h);
            flipColors(h);
        }
        return h;
    }

    // 从左边"借"红色节点
    private Node moveRedRight(Node h) {
        // assert (h != null);
        // assert isRed(h) && !isRed(h.right) && !isRed(h.right.left);
        flipColors(h);
        if (isRed(h.left.left)) {
            h = rotateRight(h);
            flipColors(h);
        }
        return h;
    }
    /**
     * 获取table的最小值
     * @return
     * @throws NoSuchElementException 
     */
    public Key min() {
        if (isEmpty()) throw new NoSuchElementException("calls min() with empty symbol table");
        return min(root).key;
    }

    // 获取当前节点的最小值
    private Node min(Node x) {
        // assert x != null;
        if (x.left == null) return x;
        else                return min(x.left);
    }




}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值