每周源码:如何构建一棵红黑树(分析TreeMap源码)

如何构建一棵红黑树(分析TreeMap源码)

TreeMap源码

  • 如何构建一棵红黑树(分析TreeMap源码)
    • 二叉树
    • 二叉搜索树
    • 平衡二叉树
    • 红黑树
      • 五个性质
      • 红黑树的插入
        • 染色
        • 旋转
          • 左转
          • 右转
    • 代码实现
      • 构建树
          • 变量
          • 定义Entry
          • 定义put方法
            • root节点
            • 寻找parent节点
            • 插入
      • 调整
            • 调整条件
            • 染色操作
            • 旋转操作
            • 为什么要旋转?
      • 源码

之前一直就想写一篇关于红黑树的文章,JAVA在JDK1.8在hashMap引入了红黑树,解决特殊情况下当链表过程导致的查询过慢的问题。
在JDK1.7中,我们都知道hashMap使用"数组"+链表的数据结构实现,使用hashcode取模获取下标数组下标时,如果在hashCode特别差的情况下,很多hashCode相同时候,这个链表可能会很长,那么使用put/get操作都可能会遍历这个链表,这样最时间复杂度在最差情况为O(n)。

虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

以上百度百科对红黑树的描述,提到了红黑树复杂,但是查询时间复杂度在O(log n)。

在聊红黑树之前,我们需要理解几个概念,二叉树,二叉搜索树以及平衡二叉树。

二叉树

二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
二叉树的结构使用代码可以这么表示

class Entry<K, V> {
 K key;
V value;
Entry left;
Entry right;
}

二叉搜索树

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值

这是二叉搜索树的特点,左节点的元素比当前节点的小,右节点的元素比当前元素大。
如果我们要搜索某个元素,只要对比它比根节点的大小,比根节点大就在右子树,比根节点小就在左子树

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第1张图片
虽然,正常情况下,二叉搜索树查找的时间复杂度为 O(log n),但是在元素递增的情况下,二叉搜索树会退化为一个链表结构,搜索的时间复杂度为O(n) 。

平衡二叉树

平衡树(Balance Tree,BT) ,它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树,除此之外,AVL树还必须是一个二叉搜索树。
满足以上2个条件,二叉树就不会出现链表结构,但是,平衡二叉树的维护高度平衡所付出的代价比从中获得的效率收益还大。

红黑树

红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。当搜索,插入,删除操作较多的情况下,使用红黑树的成本会比AVL树小很多

五个性质

  • 节点是红色或黑色。
  • 根节点是黑色。
  • 所有叶子都是黑色。(叶子是NUIL节点)。
  • 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

前三个性质是相当于是对红黑树每个节点的约束,后面两个属性确保了红黑树的平衡,

  • 规定路径上不能有两个连续的红色节点。这样最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。
  • 所有最长的路径都有相同数目的黑色节点,这就使得没有路径能多于任何其他路径的两倍。

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第2张图片

红黑树的插入

红黑树的插入和二叉搜索树的插入一致,左节点的元素比当前节点的小,右节点的元素比当前元素大。当插入一个新元素时,红黑树可以通过染色和旋转两种方式,来维护第四、第五特性。

染色

一般情况下,红黑树的新插入的节点都是红色,从第五特性知道,从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。当插入一个黑色节点,必定会破坏规则,插入一个红色节点,除非父节点时一个红色,降低破坏红黑树的可能性。
如图在
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第3张图片

如图插入节点100,会导致违背第四特性红色节点子节点必须是黑色节点
这里我们可以尝试将90和60节点染黑,同时70节点染红的方式,调整树平衡
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第4张图片

旋转

当我们无法使用染色的方式,如图,插入属性为5节点每周源码:如何构建一棵红黑树(分析TreeMap源码)_第5张图片

我们尝试将10和20节点变色,调整都会破坏第五、第四特性,
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第6张图片
这个时候就需要通过右转进行调整了,同时进行染色调整,最后调整的效果如下图

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第7张图片

左转

逆时针旋转两个节点,让父节点被其右子节点取代,而父节点成为右子节点的左子节点。
左旋的gif展示(图片来自网络):
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第8张图片

右转

右旋的gif展示(图片来自网络):
顺时针旋转两个节点,让父节点被其左子节点取代,而父节点成为右子节点的右子节点。
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第9张图片
在上面的具体的染色和旋转过程中,我没有具体描述如何实现步骤,大家可以参考我画了近百张图来理解红黑树],详细描述了有关旋转这块问题。或者跟着接下来对TreeMap的源码分析,什么时候是染色操作,什么时候是旋转操作。

代码实现

接下来就是我们这次重点,如何实现一个TreeMap(红黑树)

TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的
Comparator 进行排序,具体取决于使用的构造方法。

源码中TreeMap是继承了AbstractMap类,在分析的时候,直接新建一个类

public class tree<K, V> {
}

构建树

变量
//根节点
 private transient Entry<K, V> root;
 //定义节点黑色
 private static final boolean BLACK = true;
 //定义节点红色
private static final boolean RED = false;
//map的长度
 private static int size;

从 TreeMap的描述可以知道,map可以根据Comparator进行判断,所以我们定义一个变量comparator

private final Comparator<? super K> comparator;

同时,我们直接写好构造方法

//如果外部实现了Comparator类,采用传入的Comparator进行排序
public tree(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
//默认Comparator为null
    public tree() {
        this.comparator = null;
    }
定义Entry
tatic final class Entry<K, V> {
        //key
        private K key;
        //值
        private V value;
        //左子树
        private Entry left;
        //右子树
        private Entry right;
        //父节点
        private Entry parent;
        //默认树的节点都是黑,即null为黑
        boolean color = BLACK;
       // 构造方法
        public Entry() {
        }
        public Entry(K key, V value, Entry parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

   }

其中在源码有个小细节,当我们更新值得时候会返回旧值,这就表明了put是有返回值的,当发生更新值时候,返回旧值。

 public V setValue(V value) {
            V oldv = this.value;
            this.value = value;
            return oldv;
        }
定义put方法

由于我们只需要了解红黑树的构建过程,只实现了put方法,其实红黑树的删除也是一个复杂的过程,也涉及到了染色和旋转,下次专门写一个文章关于红黑树的删除。

 public V put(K key, V value) {
 }
root节点

当根节点不存在的时候,我们需要构建一个根节点

   Entry<K, V> r = root;
        //根节点
        if (root == null) {
            //判断是否为空
            compare(key, key);
            root = new Entry<>(key, value, null);
            size = 1;
            return null;
        }

compare方法即能满足对空值的判断,也能对两个key进行判断,这个方法是我源码最喜欢的方法之一,以后编码的时候借鉴

private int compare(Object k1, Object k2) {
//这里要求了key是实现了Comparable接口,如果有实现那就调用实现的对比,如果没有那就使用key的实现的compareTo对比方法
        return comparator == null ?
         ((Comparable<? super K>) k1).compareTo((K) k2) :
        comparator.compare((K) k1, (K) k2);
    }
寻找parent节点
  Entry<K, V> parent;
        int cmp;
        Comparator<? super K> compare = comparator;
        if (compare != null) {
            do {
                parent = r;
                cmp = compare.compare(key, r.key);
                if (cmp < 0) {
                    r = r.left;
                } else if (cmp > 0) {
                    r = r.right;
                } else
                    return r.setValue(value);
            } while (r != null);
        } else {
            if (key == null) {
                throw new NullPointerException();
            }
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = r;
                cmp = k.compareTo(r.key);
                if (cmp < 0) {
                    r = r.left;
                } else if (cmp > 0) {
                    r = r.right;
                } else
                    return r.setValue(value);
            } while (r != null);
        }

这块代码主要是找到插入节点的父节点,通过对比当前的插入的key和当前的key对比大小,判断插入的节点在当前节点的右边还是左边,这里和二叉搜索树的插入类似

 Entry<K, V> parent;
 int cmp;
 ......
 do {
parent = r;
cmp = k.compareTo(r.key);
if (cmp < 0) {
//小于0在左边
r = r.left;
 } else if (cmp > 0) {
 //对于0在右边
r = r.right;
} else
return r.setValue(value);
 } while (r != null);
插入

将新插入节点插入到父节点上

 Entry<K, V> e = new Entry<>(key, value, parent);
if (cmp < 0) {
parent.left = e;
} else {
 parent.right = e;
}
 size++;

调整

前面的部分都是搜索二叉树的拆入,当我们节点插入到树上后,会不会对节点的平衡影响?这个时候就要对插入的节点,进行调整

private void fixAfterInsertion(Entry<K, V> x) {
 x.color = RED;
//调整方法
{
 }
 root.color =  BLACK;
}

这里是根据默认插入的规则,新节点都是红色节点,以及第三特性,根节点都是黑色,我们直接将最简单的代码先实现。

调整条件
while (x != null && x != root && x.parent.color == RED) {
}

只要当前元素的父类也是红色,我们才需要进行调整,为什么这么说,红色节点的插入到黑色节点后,并不会破坏树的平衡。
如图
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第10张图片
插入65节点,并没有对整个树造成影响

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第11张图片
插入22节点,由于父类是红色节点,破了第四特性。需要进行调整

染色操作

1.判断当前父节点,是在爷节点(父节点的父节点)的左边还是右边,这里会涉及到后面是左旋还是右旋,染色操作和当前节点是否左节点还是右节点无关联。

if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {}

这里源码中封装的几个方法

//返回节点右边节点
 private static <K, V> Entry<K, V> rightOf(Entry<K, V> x) {
        return x == null ? null : x.left;
    }
//返回节点的左节点
    private static <K, V> Entry<K, V> leftOf(Entry<K, V> x) {
        return x == null ? null : x.left;
    }
//返回节点父节点
    private static <K, V> Entry<K, V> parentOf(Entry<K, V> x) {
        return x == null ? null : x.parent;
    }
    //设置颜色
private <K, V> void setColor(Entry<K, V> x, boolean color) {
        if (x != null) {
            x.color = color;
        }
    }
  //获取颜色 默认空节点返回黑色节点,符合第四特性
    private <K, V> boolean colorOf(Entry<K, V> x) {
        return (x == null ? BLACK : x.color);
    }

染色的操作都和叔节点的颜色有关系,当父节点和叔节点都是红色,我们需要将父节点和叔节染黑,同时将爷节点染红,返回爷节点,继续进行下个循环的判断

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

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第12张图片
插入34节点,然后染色
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第13张图片
为什么要这么操作?我们可以将染色理解为通过颜色变化完成对黑节个数控制的操作;红色节点可以理解为黑色节点的备胎,当我们需要增加黑节节点,它变黑。
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第14张图片
我们可以理解为这样一个思路:

  1. 34和35节点必须有个节点变黑,破坏第四特性。
  2. 假设35变黑,左边增加了一个黑色节点,破坏了40节点的第五特性,这时我们在右边也增加一个黑色节点(48变黑),保证了40节点的第五特性,
  3. 但是破坏了40父节点的第五特性,每个路径增加一个黑色节点,
  4. 这个时候,我们就要想办法保证40父节点的第五特性,也要保证40节点的平衡,那就去掉一个路径上共同节点40。

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第15张图片
虽然40节点和30节点都是红色,我们继续把40节点抛出,进行下一轮的遍历处理

我们得出进行染色的条件:父节点和叔节点是红色
染色的操作流程为

1. 将父节点和叔节点染黑
2. 将爷节点染红
3. 将循环的节点变为爷节点

当30和40都变红时,我们发现满足染色的条件:父节点和叔节点是红色,进行染色
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第16张图片
跳出循环时,根节点变黑,相当于全路径添加一个黑色节点,平衡不受影响
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第17张图片

旋转操作

说完然后,我们就考虑染色无法解决的问题
染色的条件为:父节点和叔节点是红色
如图,叔节点为为黑色,当我们进行变色操作时,节点90右路径增加一个黑色节点,叔节点无法变色,不满足第三特性(叶节点都是黑色)。这里我们就要考虑创造变色的条件==>旋转
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第18张图片
变色的条件时叔节点为红色,目前只要92和100是连接的红色节点,那我们通过旋转让100变为红色叔节点,

  1. 92节点只能右旋,需要满足二叉搜索树
    每周源码:如何构建一棵红黑树(分析TreeMap源码)_第19张图片
  2. 92节点再左旋,使得90成为其左子树

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第20张图片
这样100变成了92节点的右节点,90是91的左节点,然后染色保证平衡

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第21张图片

 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
 ......
 }
else{
 Entry<K, V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y)  == RED) {
 ......
}else{
//当前节点是父节点的左子树,需要右旋
if(x==leftOf(parentOf(x))){
x=parentOf(x);
rotateRight(x);
}
//此时x节点为旋转前父节点,通过右旋,父节点在插入节点的右字子树上,参考下图
setColor(parentOf(x),BLACK);
setColor(parentOf(parentOf(x)),RED);
rotateLeft(parentOf(parentOf(x)));
}
}

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第22张图片

这里我们看一下源码如何实现左旋和右旋的,参考之前的动图

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第23张图片

  private void rotateRight(Entry<K,V> p) {
        if (p != null) {
            Entry<K, V> l = p.left;
            p.left = l.right;
            if (l.right != null) {
                //更换父节点
                l.right.parent = p;
            }
            //互换父节点
            l.parent = p.parent;
            if (p.parent == null) {
                root = l;
               
            } else if (p == leftOf(parentOf(p))) {
                parentOf(p).left = l;
            } else {
                parentOf(p).right = l;
            }
            //p的节点换成l
            p.parent = l;
            //l的左边节点换成p
            l.right = p;
        }
    }

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第24张图片

  private void rotateLeft(Entry<K,V> p) {
        if (p != null) {
            Entry<K, V> r = p.right;
            p.right = r.left;
            if (r.left != null) {
                //更换父节点
                r.left.parent = p;
            }
            //互换父节点
            r.parent = p.parent;
            if (p.parent == null) {
                root = r;
            } else if (p == leftOf(parentOf(p))) {
                parentOf(p).left = r;
            } else {
                parentOf(p).right = r;
            }
            //p的节点换成l
            p.parent = r;
            //l的左边节点换成p
            r.left = p;
        }
    }
为什么要旋转?

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第25张图片

如图我们插入一个35节点到34节点右子树上,我们放大影响平衡的节点

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第26张图片
我们分析一下思路:
35节点和34节点肯定有一个节点需要变黑,才能不违背第四特性(每个红色节点的两个子节点都是黑色);
当35和34其中一个节点变黑会影响37节点的第五特性(从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点);37节点的左边增加一个黑色节点,那就需要减少一个黑色节点保证37节点的平衡,有两个思路

  1. 37变红,减少两边节点的黑色个数,保证平衡。*违背第四特性,这思路pass
  2. 那就减少37右子树的黑色节点个数,但是37右子树是null,参考红黑树第五特性,插入节点的位置的叔节点大部分情况都是null,这个思路也pass.
    什么样子的红黑树才是最适合变色的?
    参照染色的规则,叔节点和父亲节点都是红色,那就思考为什么这个结构是可以染色解决问题?

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第27张图片
如图我们添加一个15节点到20节点的左子树
当我们20和15节点有一个节点必须变黑的时,10节点的右子树增加一个黑色节点,那就必须增加或者减少黑色节点保证平衡。
1.增加5节点为黑色,10节点的左右路径上各增加黑色
2.需要减少一个公共的节点黑色,保证左右路径平衡,10变红
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第28张图片
从这个变色思路,我们得出一个规律
1.只在在影响平衡的节点上进行调整,一般是三个
2.调整不平衡节点为一个二叉搜索树,并且左右有一个节点是红色,方便染色操作

分析完规律后,我们回到之前的话题,怎么调整下面这个树每周源码:如何构建一棵红黑树(分析TreeMap源码)_第29张图片

1.找出影响的节点,37-34-35
2.调整不平衡节点为一个二叉搜索树
我们去掉其他树的影响,35-34-33 可以构建成这样一个二叉搜索树

 35
/  \  
34  37

第一步,左旋使得34变成35节点的左子树

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第30张图片
第二步右旋,使得37变成35节点的右子树
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第31张图片

第三步染色
1.35和34必须有一个节点变黑,保证第四特性
2.只能35节点变黑,左子树无影响,但是其右路径37是黑色,影响了平衡,将37染红
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第32张图片
调整完毕!!!

//如果当前节点在父节点的右边,大于父节点,在构建二叉树的取一个中间值作为父节点
if(x==rightOf(parentOf(x))){
x=parentOf(x);
rotateLeft(x);
 }

如果插入节点是x是父节点的右边,根据二叉搜索树的原则,影响的三个节点大小关系
parentOf(x)x

parentOf(parentOf(x))
   /
parentOf(x)
          \   
           x

构建新的二叉树时,需要x作为父节点,需要进行调整
一般思路是这样的;x在parentOf(parentOf(x))在左子树parentOf(x)的右子树上,需要进行左右节点旋转。根据需要调整的节点在爷节点的位置,进行节点旋转,具体可以查看之前提到那个文章。

setColor(parentOf(x),BLACK);
setColor(parentOf(parentOf(x)),RED);
rotateRight(parentOf(parentOf(x)));

在源码中,是先进行调整颜色再进行旋转的,这个思路我们可以这么理解
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第33张图片
如图插入14节点,由于14 -15-20已经满足二叉搜索的树条件,直接右旋20节点就会变成这样一个二叉树
每周源码:如何构建一棵红黑树(分析TreeMap源码)_第34张图片
14和15节点肯定要变黑一个,14和20肯定是15的左右子树;
为了保证红黑树的平衡,增加一个黑色节点,就需要变色一个黑色节点减少影响,由于10节点是红色,只能15变黑,让两个子树中减少一个黑色节点,即20变红。
这里为什么10是红色节点,大家可以根据第四和第五特性自己构建一个红黑树,发现只要发生旋转的节点,必定其10节点这个位置是一个红节点。感觉到了红黑树的神奇之处。
那么,可以得出一个规律,当满足构建二叉树的条件时,从最下面的节点的角度看,我们可以将父节点设置为黑色,爷节点设置为红色,再旋转就可以平衡了。

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第35张图片
最终树的结构变成了

每周源码:如何构建一棵红黑树(分析TreeMap源码)_第36张图片
附上源码结尾:

源码

public class tree<K, V> {

    private static final boolean BLACK = true;

    private static final boolean RED = false;

    private static int size;

    /**
     * 根节点
     */
    private transient Entry<K, V> root;

    //对比

    private final Comparator<? super K> comparator;

    /**
     * 构造方法
     *
     * @param comparator
     */
    public tree(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

    public tree() {
        this.comparator = null;
    }

    public V put(K key, V value) {
        Entry<K, V> r = root;
        //根节点
        if (root == null) {
            //判断是否为空
            compare(key, key);
            root = new Entry<>(key, value, null);
            size = 1;
            return null;
        }
        Entry<K, V> parent;
        int cmp;
        Comparator<? super K> compare = comparator;
        if (compare != null) {
            do {
                parent = r;
                cmp = compare.compare(key, r.key);
                if (cmp < 0) {
                    r = r.left;
                } else if (cmp > 0) {
                    r = r.right;
                } else
                    return r.setValue(value);
            } while (r != null);
        } else {
            if (key == null) {
                throw new NullPointerException();
            }
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = r;
                cmp = k.compareTo(r.key);
                if (cmp < 0) {
                    r = r.left;
                } else if (cmp > 0) {
                    r = r.right;
                } else
                    return r.setValue(value);
            } while (r != null);
        }
        Entry<K, V> e = new Entry<>(key, value, parent);
        if (cmp < 0) {
            parent.left = e;
        } else {
            parent.right = e;
        }
        size++;
        //调整树
        fixAfterInsertion(e);
        return null;
    }

    //调整树结构为红黑树
    private void fixAfterInsertion(Entry<K, V> x) {
        //默认插入的节点都是红色
        x.color = RED;
        while (x != null && x != root && x.parent.color == RED) {
            //判断当前节点是父亲节点的左节点
            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;
    }
//左旋
    private void rotateLeft(Entry<K,V> p) {
        if (p != null) {
            Entry<K, V> r = p.right;
            p.right = r.left;
            if (r.left != null) {
                //更换父节点
                r.left.parent = p;
            }
            //互换父节点
            r.parent = p.parent;
            if (p.parent == null) {
                root = r;
            } else if (p == leftOf(parentOf(p))) {
                parentOf(p).left = r;
            } else {
                parentOf(p).right = r;
            }
            //p的节点换成l
            p.parent = r;
            //l的左边节点换成p
            r.left = p;
        }
    }
//右旋
    private void rotateRight(Entry<K,V> p) {
        if (p != null) {
            Entry<K, V> l = p.left;
            p.left = l.right;
            if (l.right != null) {
                //更换父节点
                l.right.parent = p;
            }
            //互换父节点
            l.parent = p.parent;
            if (p.parent == null) {
                root = l;
            } else if (p == leftOf(parentOf(p))) {
                parentOf(p).left = l;
            } else {
                parentOf(p).right = l;
            }
            //p的节点换成l
            p.parent = l;
            //l的左边节点换成p
            l.right = p;
        }
    }

    private <K, V> void setColor(Entry<K, V> x, boolean color) {
        if (x != null) {
            x.color = color;
        }
    }

    private <K, V> boolean colorOf(Entry<K, V> x) {
        return (x == null ? BLACK : x.color);
    }

    private static <K, V> Entry<K, V> rightOf(Entry<K, V> x) {
        return x == null ? null : x.left;
    }

    private static <K, V> Entry<K, V> leftOf(Entry<K, V> x) {
        return x == null ? null : x.left;
    }

    private static <K, V> Entry<K, V> parentOf(Entry<K, V> x) {
        return x == null ? null : x.parent;
    }

    private int compare(Object k1, Object k2) {
        return comparator == null ? ((Comparable<? super K>) k1).compareTo((K) k2) :
                comparator.compare((K) k1, (K) k2);
    }

    /**
     * 1.变量
     * 2.构造树
     * 3.调整树
     */
//树的对象
    static final class Entry<K, V> {
        private K key;
        private V value;
        private Entry left;
        private Entry right;
        private Entry parent;
        //默认树的节点都是黑,即null为黑
        boolean color = BLACK;

        public Entry() {
        }

        public Entry(K key, V value, Entry parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            V oldv = this.value;
            this.value = value;
            return oldv;
        }

    }


    public static void main(String[] args) {
        tree<Integer, Integer> tree = new tree();
        tree.put(0, 0);
        tree.put(-1, -1);
        tree.put(1, 1);
        tree.put(2, 2);
        tree.put(3, 3);
        tree.put(4, 5);
    }


}

第一次写这么长的篇幅描述自己对源码的理解,存在错别字或者语法问题,后期,我会慢慢校验!!!

你可能感兴趣的:(红黑树)