本章节主要分析红黑树的“插入算法”和“获取算法”,也就是put()方法和get()方法的底层实现。
在学习TreeMap的put()方法之前,我们首先应该创建一个叶子节点Entry类,叶子节点Entry是TreeMap的内部类,它有几个重要的属性:节点的颜色、左子节点指针、右子节点指针、父节点指针、节点的值。
class MyTreeMap<K, V> {
// 节点类
class Entry<K, V> {
// 键
K key;
// 值
V value;
// 左孩子
Entry<K, V> left;
// 右孩子
Entry<K, V> right;
// 父亲
Entry<K, V> parent;
// 颜色
boolean color = BLACK;
// 构造方法
public Entry(K key, V value, Entry<K, V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
}
}
TreeMap中同时也包含了如下几个重要的属性:
class MyTreeMap<K, V> {
// 比较器,通过comparator接口我们可以对TreeMap的内部排序进行精密的控制
private final Comparator<? super K> comparator;
// 红-黑树根节点
private Entry<K, V> root;
// 容器大小
private int size;
// 红黑树的节点颜色--红色
private static final boolean RED = false;
// 红黑树的节点颜色--黑色
private static final boolean BLACK = true;
// 此处省略Entry节点类
}
最后,我们再添加两个构造方法,一个无参构造,另外一个有参构造。
class MyTreeMap<K, V> {
// 此处省略MyTreeMap属性
// 无参构造方法
public MyTreeMap() {
this.comparator = null; // 默认比较机制
}
// 有参构造方法
public MyTreeMap(Comparator<? super K> comparator) {
this.comparator = comparator; // 自定义比较器的构造方法
}
// 此处省略Entry节点类
}
到此处,TreeMap的put()方法的准备工作就已经完成,那么接下来我们就开始分析并实现put()方法。
- **put()**方法实现
在TreeMap的put()的实现方法中主要分为两个步骤,第一步:构建排序二叉树,第二步:保持红黑树平衡。
- 第一步:构建排序二叉树
对于排序二叉树的创建,其添加节点的过程如下:
(1) 以根节点为初始节点进行检索。
(2) 与当前节点进行比对,若新增节点值较大,则以当前节点的右子节点作为新的当前节点。否则以当前节点的左子节点作为新的当前节点。
(3) 循环递归2步骤知道检索出合适的叶子节点为止。
(4) 将新增节点与3步骤中找到的节点进行比对,如果新增节点较大,则添加为右子节点;否则添加为左子节点。
按照这个步骤我们就可以将一个新增节点添加到排序二叉树中合适的位置。如下:
class MyTreeMap<K, V> {
// 此处省略MyTreeMap属性和构造方法
/**
* 添加元素
* 步骤一:构建排序二叉树
* 步骤二:保持红黑树平衡
*/
public V put(K key, V value) {
// 步骤一:构建排序二叉树
// 1.当红-黑树是空树,把新创建的节点设置为跟节点
if (null == root) {
// 当root为null,证明是空树
// 创建一个Entry节点,并设置为根节点
root = new Entry<K, V>(key, value, null);
// 结合元素个数设置为1
size = 1;
return null;
}
// 2.当红-黑数不为空树,则找到新添加节点的父节点
// 用t表示二叉树的当前节点
Entry<K, V> t = root;
// 用parent表示父节点,并作为新添加节点的父节点
Entry<K, V> parent;
// 使用compare表示key排序的返回结果
int compare;
// 2.1如果comparator不为null,则采用外部比较器进行创建TreeMap集合
if (null != comparator) {
do {
// parent指向上次循环后的t
parent = t;
// 比较新增节点的key和当前节点key的大小
compare = comparator.compare(key, t.key);
// compare返回值小于0,表示新增节点的key小于当前节点的key
// 则以当前节点的左子节点作为新的当前节点
if (compare < 0) {
t = t.left;
}
// compare返回值大于0,表示新增节点的key大于当前节点的key
// 则以当前节点的右子节点作为新的当前节点
else if (compare > 0