数据结构---红黑树

红黑树怎么来?历史缘由

先说二叉树,父节点劈叉出两个节点,就两个,不可以多。这样一层一层下去。

再说二叉查找树(基于二分查找),同样有上面的劈叉属性,但是呢左中右有大小顺序,左边小于中间,中间小于右边。

接着就是红黑树,干嘛上面下来就红黑,二叉树如果不加以控制就成单边树了。因为二叉树没规定不可以单边开叉,而且一直往下开叉,5左单边4再左单边3,再左单边2,类似的就失去了二叉查找树的意义了。这下红黑就出来了。

红黑为了达到楼上的特点就列出了五个特点

一、节点必须红黑两种颜色。treemap就用了boolean值分别表示

二、根节点必须黑色

三、没个尾节点下面是黑色节点(感觉没啥意义)

四、红色节点下面或者上面不可以连续两个出现

五、任意节点到他子节点经过的黑色节点个数一样多。

那这玩意搞着五个条件无非就是限制插入,删除。有人破坏树的情况下,限制的。

例如,插入一个节点,首先二分查找节点,再fix。fix就为了满足这五个规则。一般都是不符合后面这两条。红色不可以连着,任意节点到子节点黑色同样个数。

jdk经典案例,treeset、treemap。上酒,小二。

就treemap源码分析。

一、treemap内部类子节点除了map中的key和value,还有左右节点和父节点,最后特有颜色(boolean值),遍历都是一样的。

static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left = null;
        Entry<K,V> right = null;
        Entry<K,V> parent;
        boolean color = BLACK;
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

二、put查找出要插入的位置,也就是父节点。  fixAfterInsertion(e);这里是重点。

    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

三、重点  fixAfterInsertion(e);

private void fixAfterInsertion(Entry<K,V> x) {

//直接红色,就要搞破坏,黑色就没啥了
        x.color = RED;

//新增节点非空,且不是根节点,且新增节点(自己,以下称呼自己)父亲是红色,如果父亲是黑色也不搞事。

        while (x != null && x != root && x.parent.color == RED) {

//父亲跟父亲的父亲的左节点equal?也就是父亲是否为祖父的左节点
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {

//祖父右节点,叔叔
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));

//叔叔颜色红色,跟父亲红色一样,省事,父亲和叔叔一起搞成黑色,祖父搞成黑色,再以祖父为自己,继续循环遍历
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));//以祖父为自己,遍历
                } else {

//叔叔颜色为黑色

//自己是父亲的右节点要左转
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }

//变换父亲(黑色)和祖父颜色(红色)
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);

//右转祖父
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));

//叔叔为红色跟楼上一样,做同样操作,说明无论父亲是左,还是右,叔叔跟父亲要是同样颜色,一样变色就可以。
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {

//父亲是祖父的右,自己是父亲的左,右转。楼上跟这个就是反着来。父亲是祖父的左,自己是父亲的右,左转
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }

//跟上面的操作一样。父亲黑色,祖父红色
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);

//左转祖父(发现都是左右转交替)
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

不强求记,毕竟像红黑树,外面大把的实现。

可以画图,进一步分析为啥,等有空再搞

猜你喜欢

转载自blog.csdn.net/huangddy/article/details/83628837