一、红黑树
1.红黑树在Java中的应用
以TreeMap为例,了解红黑树在Java中的应用,TreeMap的继承关系如下:
其中Map接口源码:
//键值对类型的顶层接口, 通过键(key)来映射到值(value),key和value是对应的关系,因此key不能重复(若果重复将不能保证每次找到的value是相同的)。key不能相同,但value却可以不唯一。
public interface Map<K,V> {
//map中包含的键值对数量
int size();
//map是否为空
boolean isEmpty();
//map中是否有含有键为key的键值对
boolean containsKey(Object key);
//map中是否含有值为value的价值对
boolean containsValue(Object value);
//返回键为key的值值对象
V get(Object key);
//存放键值对
V put(K key, V value);
//删除键为key的键值对
V remove(Object key);
//将m中的所有键值对存放到当前map中
void putAll(Map<? extends K, ? extends V> m);
//清空map中的所有键值对
void clear();
//返回map中所有key组成的set集合
Set<K> keySet();
//返回map中所有value组成的collection集合
Collection<V> values();
//返回map中所有键值对的set集合
Set<Map.Entry<K, V>> entrySet();
//Entry接口,Map接口的内部接口,是存放键值对的对象接口,每个Entry表示一个键值对
interface Entry<K,V> {
//返回该entry的key值
K getKey();
//返回该entry的value值
V getValue();
//改变entry的value值
V setValue(V value);
//entry的equals方法,用于比较两个entr是否相同
boolean equals(Object o);
//hash值
int hashCode();
//返回一个用于比较entry的comparable实现,通过比较entry的key来确定entry之间的大小。
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey()); //lambda表达式写法的comparable匿名实现。
}
//返回一个用于比较entry的comparable实现,通过比较entry的value来确定entry之间的大小。
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue()); //lambda表达式写法的comparable匿名实现。
}
//返回一个用于比较entry的比较器,该比较器是通过比较entry的key来确定entry之间的大小。
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); //lambda表达式写法的comparator匿名实现。
}
//返回一个用于比较entry的比较器,该比较器是通过比较entry的value来确定entry之间的大小。
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
}
//map的equals方法,判断两个map中的键值对集合是不是相同
boolean equals(Object o);
//map的hash值,hash值为每个价值对的hash值之和。
int hashCode();
//默认方法(jdk1.8新增),map中存在键为key的value就返回该value,不存在则返回一个默认值defaultValue
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
//forEach方法,其效果等同于for(Entry entry:Map.entrySet()){ //执行语句 };即遍历map中的entryset。
//对lambda表达式感兴趣可以看看java.util.function包学习,或查看其它资料
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v); //执行某个操作(即你遍历该map想要干什么)
}
}
//遍历整个map中的entry,将所有key的value值替换为某个新值,新值由function对象的apply方法的返回值决定。
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) { //遍历entrset
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
throw new ConcurrentModificationException(ise);
}
v = function.apply(k, v); //将所有value值替换为apply的返回值
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
throw new ConcurrentModificationException(ise);
}
}
}
//如果key在map中不存在,就向map中新增该键值对,否则将map已存在的key对应的value返回。
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
//若要删除的键值对的value与map中的key对应的value一致时删除该键值对,否则不删除
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
//将key对应value值由oldvalue替换成newValue,若map中key对应的值不为oldValue,则不执行替换操作。
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}
//将map中原有的键值对替换成新传入的值
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
//若key对应的value为null,则将value替换成mappingFunction中apply方法的返回值
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
//若key在map中存在且对应的value值不为null,则将value替换成remappingFunction中apply()方法的返回值
default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) { //存在且非空
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
}
//若remappingFunction的apply()方法的返回值不为null,就更新key的value为其返回值;否则删除key对应的键值对。
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
}
//若在map中key不存在或对应的value为null,则令其与remappingFunction的apply方法的返回值或者传入的value值相关联成键值对
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
//newValue的值与oldValue是否为null相关,oldValue为null,则newValue为传入的value参数,否则为apply方法的返回值
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key); //newValue为null,则删除key
} else {
put(key, newValue); //更新key
}
return newValue;
}
}
SortedMap接口的源码:
//Map的子接口,提供过了若干个与排序相关的方法,使其能够进行排序
public interface SortedMap<K,V> extends Map<K,V> {
//返回此map中用于key排序的比较器,若使用的是key本身的comparable方法来比较,则返回null;
Comparator<? super K> comparator();
//返回子集合,从fromKey到toKey的子集合(左开右闭)
SortedMap<K,V> subMap(K fromKey, K toKey);
//返回包含所有key值小于toKey的子集合
SortedMap<K,V> headMap(K toKey);
//返回包含所有key值大等于fromKey的子集合
SortedMap<K,V> tailMap(K fromKey);
//返回最小key值
K firstKey();
//返回最大key值
K lastKey();
//返回key值的set集合
Set<K> keySet();
//返回所有value的集合
Collection<V> values();
//返回所有entry集合
Set<Map.Entry<K, V>> entrySet();
}
NavigableMap接口源码:
//SortedMap的子接口,主要扩展了一些便于匹配的导航方法,且主要是为了查找而不是遍历
public interface NavigableMap<K,V> extends SortedMap<K,V> {
//返回以键小于key的entry,且该entry的键值是所有键值小于key中最大的,不存在则返回null
Map.Entry<K,V> lowerEntry(K key);
//返回所有键值中小于key的最大键值。
K lowerKey(K key);
//返回键值小于等于key的最大键值的entry
Map.Entry<K,V> floorEntry(K key);
//返回键值小于等于key的最大键值
K floorKey(K key);
//返回键值大于等于key的最大键值的entry
Map.Entry<K,V> ceilingEntry(K key);
//返回键值大于等于key的最大键值
K ceilingKey(K key);
//返回键值大于key的最大键值的entry
Map.Entry<K,V> higherEntry(K key);
//返回键值大于key的最大键值
K higherKey(K key);
//返回key最小的entry
Map.Entry<K,V> firstEntry();
//返回key最大的entry
Map.Entry<K,V> lastEntry();
//返回并删除key最小的entry
Map.Entry<K,V> pollFirstEntry();
//返回并删除key最大的entry
Map.Entry<K,V> pollLastEntry();
//返回key逆向排序的map
NavigableMap<K,V> descendingMap();
//返回所有key的set集合
NavigableSet<K> navigableKeySet();
//返回key逆向排序的key的set集合
NavigableSet<K> descendingKeySet();
//返回一个键值从fromKey到toKey的子map,是否包含fromKey和toKey由fromInclusive和toInclusive决定
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
//返回一个键值小于等于fromKey的子map(inclusive决定是否包含有fromKey)
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
//返回一个键值大于等于toKey的子map(inclusive决定是否包含有toKey)
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
//返回一个键值从fromKey到toKey的子map(左开右闭)
SortedMap<K,V> subMap(K fromKey, K toKey);
//返回一个键值小于fromKey的子map
SortedMap<K,V> headMap(K toKey);
//返回一个键值大于等于toKey的子map
SortedMap<K,V> tailMap(K fromKey);
}
abstractMap的源码:
//Map的骨干抽象实现类,实现了大部分方法
public abstract class AbstractMap<K,V> implements Map<K,V> {
protected AbstractMap() {
}
public int size() {
return entrySet().size(); //直接返回entry的个数
}
public boolean isEmpty() {
return size() == 0;
}
public boolean containsValue(Object value) {
Iterator<Entry<K,V>> i = entrySet().iterator(); //获取set集合的迭代器
if (value==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getValue()==null)
return true;
}
} else {
while (i.hasNext()) { //通过迭代器遍历所有键值对,看是否存在value
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
return false;
}
public boolean containsKey(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) { //通过迭代器遍历所有键值对,看是否存在key
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
return false;
}
public V get(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) { //遍历所有键值对,取出key对应的value,不存在返回null。
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return e.getValue();
}
}
return null;
}
public V put(K key, V value) {
throw new UnsupportedOperationException(); //抽象类不支持新增操作
}
//迭代遍历查找key对应的entry,然后删除该entry
public V remove(Object key) {
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
correctEntry = e;
}
}
V oldValue = null;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
i.remove();
}
return oldValue;
}
//循环挨个将m中的键值对加到当前map中
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public void clear() {
entrySet().clear(); //调用entryset的清空方法
}
//存储所有key的set集合
transient Set<K> keySet;
//存储所有value的集合
transient Collection<V> values;
//返回key的set集合
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new AbstractSet<K>() { //匿名内部类方式实现一个keyset的抽象类
//返回一个set的迭代器
public Iterator<K> iterator() {
return new Iterator<K>() { //迭代器的匿名实现
private Iterator<Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public K next() {
return i.next().getKey();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object k) {
return AbstractMap.this.containsKey(k);
}
};
keySet = ks;
}
return ks;
}
//返回value集合,同样采用匿名实现
public Collection<V> values() {
Collection<V> vals = values;
if (vals == null) {
vals = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public V next() {
return i.next().getValue();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object v) {
return AbstractMap.this.containsValue(v);
}
};
values = vals;
}
return vals;
}
//抽象类,返回entry的set集合
public abstract Set<Entry<K,V>> entrySet();
public boolean equals(Object o) {
if (o == this) //判断是不是对象本身
return true;
if (!(o instanceof Map)) //判断是不是Map的实现类
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size()) //判断map的数量
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) { //遍历比较所有的键值的key和value
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
public int hashCode() {
int h = 0;
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
h += i.next().hashCode(); //所有entry的hashcode相加就是map的hashcode
return h;
}
protected Object clone() throws CloneNotSupportedException {
AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
result.keySet = null;
result.values = null;
return result;
}
private static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
//Map中内部接口Entry的简单实现
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = -8499721149061103585L;
//存放entry对象的key
private final K key;
//存放entry对象的value
private V value;
//构造方法
public SimpleEntry(K key, V value) {
this.key = key;
this.value = value;
}
public SimpleEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
//返回key
public K getKey() {
return key;
}
//返回value
public V getValue() {
return value;
}
//改变value
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
//判断两个entry是否相同
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
}
//也是Entry接口的实现类,但entry对象时不可改变的,即key和value确定后,不能更改
public static class SimpleImmutableEntry<K,V>
implements Entry<K,V>, java.io.Serializable
{
private static final long serialVersionUID = 7138329143949025153L;
private final K key;
private final V value;
public SimpleImmutableEntry(K key, V value) {
this.key = key;
this.value = value;
}
public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
this.key = entry.getKey();
this.value = entry.getValue();
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
//不可改变value值
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return eq(key, e.getKey()) && eq(value, e.getValue());
}
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^
(value == null ? 0 : value.hashCode());
}
}
2.红黑树定义
在看TreeMap的红黑树实现之前,先要了解什么是红黑树?
红黑树是一棵二叉树,并且是一棵自平衡的二叉查找树。为什么叫红黑树呢,因为红黑树规定:在这棵二叉查找树树中的结点必须有颜色,即每个结点必须多带一个属性color,且结点的颜色要么是红色,要么是黑色(不能有例外),因此叫红黑树。并且红黑树在原有的二叉查找树的要求上,有增加了若干要求:
-
每个节点要么是红色,要么是黑色。
-
根节点永远是黑色的。
-
所有的叶节点都是空节点(即 null),并且是黑色的。
-
每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)。
-
从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。
这些约束强化了红黑树的关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。这样就让树大致上是平衡的。因此红黑树的高度至多为2log2(n+1)。
3.红黑树结点的增加过程
红黑树的基本操作时添加,删除;每一个添加或删除操作都有可能影响到树的平衡情况。因此,在对红黑树进行增加和删除操作时要进行额外的自平衡操作。那么,红黑树是如何实现自平衡的呢?答案是通过变色和旋转实现,其中旋转即左旋和右旋。
变色:即当结点颜色不满足红黑树要求时,对结点的颜色进行改变的操作。
左旋:对一个结点的进行左旋操作意味着“将该结点变成一个左孩子”。
右旋:对一个结点的进行右旋操作意味着“将该结点变成一个右孩子”。
TreeMap的部分源码:
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
//用与key值比较的比较器
private final Comparator<? super K> comparator;
//树的根结点
private transient Entry<K,V> root;
//空构造,比较器为空
public TreeMap() {
comparator = null;
}
//带比较器的构造器
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//带初始数据的构造,且不带比较器
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
//带初始比较器的构造,比较器为m的比较器
public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
//新增键值对方法
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) { //根结点为空时,传入的key,value作为根结点
compare(key, key);
root = new Entry<>(key, value, null); //以传入的key,value新建entry
size = 1; //树的结点数加1
modCount++;
return null;
}
int cmp; //记录最后一次比较结果,即key与父结点相比是大,还是小
Entry<K,V> parent;
Comparator<? super K> cpr = comparator;
if (cpr != null) { //比较器不为null时,以key值的比较用比较器实现
do {
parent = t;
cmp = cpr.compare(key, t.key); //与t结点的key值进行比较(t从根结点开始比较)
if (cmp < 0) //传入的key值比t.key小,则令t等t的左孩子
t = t.left;
else if (cmp > 0) //传入的key值比t.key大,则令t等t的右孩子
t = t.right;
else
return t.setValue(value); //key相等,说明key已在map中存在,直接更新value后返回
} while (t != null);
}else { //比较器为null,则使用key自带的compareTo方法,即要求key为基本类型或者实现Comparable接口的类型
if (key == null)
throw new NullPointerException(); //key不能为null
@SuppressWarnings("unchecked")
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); //新建以parent为父结点的key,value的entry结点
if (cmp < 0)
parent.left = e; //比父结点小,为左孩子
else
parent.right = e; //比父结点大,为右孩子
fixAfterInsertion(e); //红黑树自平衡具体实现方法
size++;
modCount++;
return null;
}
//TreeMap自平衡的具体实现方法,即红黑树的平衡实现逻辑
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED; //默认传入的结点x为红色,因为新增结点必须是红色的,不是红色必然导致任意经过x结点到叶子结点的路径上黑色结点数比其他路径多1,即不满足条件5
//当x结点不为null,且不为根结点,且其父节点的颜色为红色时进入循环,红黑树规定,不能有父子结点颜色同时为红色。
while (x != null && x != root && x.parent.color == RED) {
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //判断x的父结点是x的爷爷结点的左孩子,还是右孩子
Entry<K,V> y = rightOf(parentOf(parentOf(x))); //y为x的爷爷结点的右孩子
//判断y结点是否为红色,即判断x的父结点的兄弟是否为红色
//若y结点也为红色,则y结点和x的父结点要变为黑色,y的父结点(即x的爷爷结点)则要变为红色
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK); //x的父结点变为黑色
setColor(y, BLACK); //y结点变为黑色
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x)); //因为x的爷爷结点变为红色,因此需要对其进行判断是否满足红黑树要求,即是否要进行变色或者旋转操作
} else {
//当y结点(空的叶子结点也为黑色)为黑色时,此时变色操作已经不能满足红黑树要求了。
//如果只用变色,要么要让每个非空结点都要进行一次变色,到最后发现根结点或叶子结点一定会出现红色,不能满足条件2,3。因此,这时要用到旋转操作了
//当x结点是其父结点的右孩子时,即x比父结点大,则对其父结点进行左旋
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x); //对父结点左旋
}
setColor(parentOf(x), BLACK); //将x的父结点变为黑色,保证不能有两个连续的红色结点
setColor(parentOf(parentOf(x)), RED); //将x结点的爷爷结点变为红色
rotateRight(parentOf(parentOf(x))); //对x的爷爷结点右旋
}
} else { //x的父结点是爷爷结点的右孩子
Entry<K,V> y = leftOf(parentOf(parentOf(x))); //令y为x的父结点的兄弟结点
if (colorOf(y) == RED) { //y为红色
setColor(parentOf(x), BLACK); //x的父结点变为黑色
setColor(y, BLACK); //y也变为黑色
setColor(parentOf(parentOf(x)), RED); //x的爷爷结点变为红色
x = parentOf(parentOf(x)); //令x为爷爷结点,以便于下次循环判断爷爷结点变红色后是否满足红黑树要求
} else { //y为黑色
if (x == leftOf(parentOf(x))) { //x结点是左孩子还是右孩子,左孩子(x<x.parent)则对x的父结点进行右旋,右孩子(x>x.parent)则不做处理
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK); //将x的父结点变黑色
setColor(parentOf(parentOf(x)), RED); //x的爷爷结点变红色
rotateLeft(parentOf(parentOf(x))); //对x的爷爷结点进行左旋
}
}
}
root.color = BLACK; //根结点要始终保持黑色
}
//左旋
private void rotateLeft(Entry<K,V> p) {
if (p != null) { //p结点不为空
Entry<K,V> r = p.right; //r为p的右孩子
p.right = r.left; //令p的右孩子为r的左孩子。因为r是p的右孩子,则有r的所有子结点都大于p但小于r,因此左旋时,要将r的左子树移动到p的右子树上。
if (r.left != null) //r的左孩子不为null时,将其父结点由r变更为p
r.left.parent = p;
r.parent = p.parent; //将r的父结点由原本的p变更为p的父结点,即r原本的爷爷结点
//若p没有父结点,则表示p原本为根结点,现在将r变为根结点(红黑树中,只有根结点和叶子结点(叶子结点为null)没有父结点,其他结点都有父结点)
if (p.parent == null)
root = r;
else if (p.parent.left == p) //p的父结点不为空时,且p为其父结点的左孩子时,令其左孩子变更为r
p.parent.left = r;
else
p.parent.right = r; //p的父结点不为空时,且p为其父结点的右孩子时,令其右孩子变更为r
r.left = p; //p变为r的左孩子
p.parent = r; //令p的父结点变为r
}
}
//右旋
private void rotateRight(Entry<K,V> p) {
if (p != null) { //p是否为空
Entry<K,V> l = p.left; //获取p的左孩子,设为l;并且由于l是p的左孩子,有了l<p
p.left = l.right; //因为了l<p,则l的所有子结点均小于p,因而对p进行右旋时,只能将l的右子树变为p左子树,以保证有序
if (l.right != null) //若l的右孩子不为null,改变l的右孩子结点的父结点为p(原本是l)。
l.right.parent = p;
l.parent = p.parent; //l的父结点有原本的p改为p的父结点
if (p.parent == null)
root = l; //p若是没有父结点,则表示p原本为root结点,右旋时,p将不再是父结点,父结点变为l。
else if (p.parent.right == p)
p.parent.right = l; //p有父结点,且p为右孩子时,l顶替p做右孩子
else
p.parent.left = l; //p有父结点,且p为左孩子时,l顶替p做左孩子
l.right = p; //p变为l的右孩子,因为l<p;p只能做右孩子
p.parent = l; //将p的父结点变成l
}
}
//对p结点进行变色操作
private static <K,V> void setColor(Entry<K,V> p, boolean c) {
if (p != null)
p.color = c;
}
//返回p结点的父结点,若p不存在则返回null
private static <K,V> Entry<K,V> parentOf(Entry<K,V> p) {
return (p == null ? null: p.parent);
}
//返回p结点的左孩子
private static <K,V> Entry<K,V> leftOf(Entry<K,V> p) {
return (p == null) ? null: p.left;
}
//返回p结点的右孩子
private static <K,V> Entry<K,V> rightOf(Entry<K,V> p) {
return (p == null) ? null: p.right;
}
//返回p结点的颜色,若为null则返回黑色
private static <K,V> boolean colorOf(Entry<K,V> p) {
return (p == null ? BLACK : p.color);
}
总的来说,在红黑树中新增结点时,默认新增结点x为红色(因为若新增结点为黑色,由于其必然占据原本为叶子(叶子结点为黑色)结点的位置,这时新增结点及其叶子结点都是黑色的,则必然使得红黑树不满足:从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点)。当x结为空结点且x不为根结点且x.parent为红色时(父子结点不能同时为红色)才需要进行自平衡操作。而自平衡操作则首先要对x.parent的位置进行区分:
第一种情况,当x.parent为左孩子(x的爷爷结点的左孩子)且其兄弟结点y的颜色也为红色时,此时只需进行变色操作,即将x.parent和y变为黑色,x的爷爷结点变为红色,然后再对变为红色的爷爷结点进行一次自平衡判断(红黑树中将结点改成红色一定要对该结点进行自平衡判断)。
第二种情况,x.parent为左孩子但其兄弟结点y的颜色为黑色,此时变色操作已经不能实现红黑树的自平衡了,需要增加旋转操作。旋转操作则要对x结点的位置(x是x.parent的左孩子还是右孩子)进去判断:1.当x是右孩子时,要对x.parent进行左旋操作(x的父结点由原本的x.parent变为现在的x.parent.parent,原本的x.parent则变为x的左孩子),然后将x(为了与源码对应,此时x指向的是原本x的父结点,即左旋操作后的左孩子为x)的父结点变为黑色,x的爷爷结点变为红色并对爷爷结点进行右旋(右旋之前有叶子结点为黑色->x为红色->x.parent为黑色->x.parent.parent为红色。此时,由于x.parent.parent原本必然是黑色,故爷爷结点的父结点必是红色,则必然不满足:父子结点不能同为红色。为了达到平衡,此时最简单的操作就是对x的爷爷结点进行右旋,黑色的x.parent结点占据x.parent.parent的位置,从而达到平衡)。2.另外,当x是左孩子时,此时x<x.parent,无需对x.parent进行左旋操作,只要将x.parent变为黑色,x.parent.parent变为红色后,对x.parent.parent进行右旋即可。
第三种情况,当x.parent为右孩子且其兄弟结点y为红色时,此时也只需进行变色操作,即将x.parent和y变为黑色,x的爷爷结点变为红色,然后再对变为红色的爷爷结点进行一次自平衡判断(红黑树中将结点改成红色一定要对该结点进行自平衡判断)。
第四种情况,当x.parent为右孩子但其兄弟结点y的颜色为黑色,此时变色操作也不能实现红黑树的自平衡了,增加旋转操作。此时也分为x是x.parent的左孩子还是右孩子的情况:1.x为左孩子时,要对x=x.parent进行右旋操作,并将此时的x.parent变为黑色,x.parent.parent变为红色,再对x.parent.parent进行左旋(与第二种情况的1是镜像操作)。2.当x是右孩子时,此时x>x.parent,无需对x.parent做右旋操作,只要将x.parent变为黑色,x.parent.parent变为红色后,对x.parent.parent进行左旋即可。
以上便是红黑上中新增操作的全部实现。而删除操作与新增一样也需要实现自平衡。
4.红黑树结点的删除过程
对于红黑树中结点的删除可分为如下若干种情形:
一、要删除的结点d为红色且左右孩子为null,这种情形d结点可以直接删除,因为此时d的删除不会影响到红黑树的平衡情况。
二、要删除的结点d为红色且仅有一个孩子(左或右),这种情形在红黑树中不可能存在(如果存在,则红黑树不可能平衡),因此没必要考虑。
三、要删除的结点d为红色且左右孩子均不为null,此时要找出d右子树中最小(或左子树中最大)的结点y,将其key,value直接保存到要删除结点d中,此时问题变为删除d右子树中的最小结点y。若y为红色,则删除问题变为一的情形,直接删除即可。若y为黑色(左右孩子中至少有一个为null且不为null的孩子结点必定是红色),则删除问题变为十二的情形。
四、要删除的结点d为黑色且左右孩子为null,且d为父结点p的左孩子,p为黑色,d的兄弟结点s为红色(如下左图)。此时需要对p进行左旋操作(如下右图),这样d结点的删除将变为六/八/十的情形。
五、要删除的结点d为黑色且左右孩子为null,且d为父结点p的右孩子,p为黑色,d的兄弟结点s为红色(如下左图)。此时需要对p进行右旋操作(如下右图),这样d结点的删除情形最终将变为七/九/十的情形。
六、要删除的结点d为黑色且左右孩子为null,且d为父结点p(p的颜色不限)的左孩子,d的兄弟结点s为黑色且s的右孩子sr为红色(如下左图)。此时需要对p进行左旋操作并将p和s的颜色互换,最后将sr变为黑色(如下右图)。
七、要删除的结点d为黑色且左右孩子为null,且d为父结点p(p的颜色不限)的右孩子,d的兄弟结点s为黑色且s的左孩子sl为红色(如下左图)。此时需要对p进行右旋操作并将p和s的颜色互换,最后将sl变为黑色(如下右图)。
八、要删除的结点d为黑色且左右孩子为null,且d为父结点p(p的颜色不限)的左孩子,d的兄弟结点s为黑色且s的右孩子为null但左孩子sl不为null(如下左图)。此时需要对s进行右旋操作并将sl和s的颜色互换(如下右图),这样就变为六的情形。
九、要删除的结点d为黑色且左右孩子为null,且d为父结点p(p的颜色不限)的右孩子,d的兄弟结点s为黑色且s的左孩子为null且sr为红色(如下左图)。此时需要对s进行左旋操作并将sr和s的颜色互换(如下右图),这样就变为七的情形。
十、要删除的结点d为黑色且左右孩子为null,且d的父结点p为红色,d的兄弟结点s为黑色(如下左图)。此时,只要将p和s的颜色互换即可。
十一、要删除的结点d为黑色且左右孩子为null,且d的父结点p为黑色,d的兄弟结点s为黑色(如下左图)。此时,删除操作必然使得经过p的路径黑色结点-1,因此只能先将d删除,s变为红色,保证p为根的树平衡,然后再以p为起点对整个红黑树进行平衡操作,这样就转变为十二的情形。
十二、要删除的结点d为黑色,且左右孩子最多只有一为null(如下图),此时,只需要选取任意不为null的孩子结点替换d结点并变为黑色即可达到平衡。
十三、要删除的结点d为根结点,且树中只有这一个结点,直接删除即可。
删除的情形就分为以上这些,下面看看TreeMap中是如何实现的:
public V remove(Object key) {
Entry<K,V> p = getEntry(key); //获取key对应的entry结点
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p); //真正执行删除的方法
return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--; //树中结点数-1
//当p结点左右孩子均不为null时,获取p在其右子树中最小结点或左子树中最大结点s,将p的key,value替换成s的key,value,此时要删除的结点就变为s。
if (p.left != null && p.right != null) {
Entry<K,V> s = successor(p); //返回p右子树中最小结点,s为最多只有一个右孩子的结点
p.key = s.key;
p.value = s.value;
p = s;
}
//replacement为p的左孩子或右孩子(没有左孩子时)
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
//待删除结点只有一孩子
if (replacement != null) {
replacement.parent = p.parent; //将p从树中删除,replacement代替p的位置
if (p.parent == null) //p为根结点的情形下,p被删除,用p的孩子结点replacement来做根结点
root = replacement;
else if (p == p.parent.left) //p为左孩子的情形
p.parent.left = replacement;
else //p为右孩子的情形
p.parent.right = replacement;
p.left = p.right = p.parent = null; //将p的所有属性赋null,与树断绝所有联系,以便于垃圾回收器回收
if (p.color == BLACK) //当p的颜色为黑色时,需要进行红黑树的自平衡操作。p为红色且只有一个孩子结点的情况不存在(不满足红黑树的要求),即情形十二
fixAfterDeletion(replacement); //红黑树删除结点后实现自平衡的方法
} else if (p.parent == null) { //树中只有根结点一个结点,即要删除的结点为根结点,情形十三
root = null;
} else { //待删除结点没有左右孩子
if (p.color == BLACK) //待删除的结点为黑色,左右孩子均为null,此时删除p必然导致红黑色不平衡,因此需要先进行平衡操作
fixAfterDeletion(p);
if (p.parent != null) { //判断p.parent是否为null,为null,将p从树中删除
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) { //返回右子树中的最小值
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else { //不执行
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) { //当要删除的结点x不为根结点且x为黑色时才需要自平衡操作
if (x == leftOf(parentOf(x))) { //x为左孩子时
Entry<K,V> sib = rightOf(parentOf(x)); //x的兄弟结点sib
if (colorOf(sib) == RED) {
//sib为红色时,即删除的情形为x为左孩子,黑色,sib为右孩子,红色,此时为对应上文中四的情形,需要将sib变黑,x.parent变红,再对x.parent左旋,
//这样,x的兄弟结点由sib变为sib的右孩子,且其为黑色,这样x的删除情形就变了。
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&colorOf(rightOf(sib)) == BLACK) {
//sib的左右孩子均为黑色,即删除情形为x为左孩子,黑色;sib为右孩子,黑色 ;sib的左右孩子均为黑色;此时对应的是十或十一的情形。
//需要将sib变为红色,在对x的父结点进行自平衡判断,若x.parent为红色,则只需变黑即可,若为黑色,则将再次进行平衡操作
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
//sib的右孩子为黑色,即删除情形为x为左孩子,黑色;sib为右孩子,黑色;sib的右孩子为黑色,左孩子为红色;此时对应的是八的情形。
//需要将sib变为红色,sib的左孩子变为黑色,再对sib进行右旋。此时x的兄弟节点有sib变为sib原本的右孩子,删除情形也变为六
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
//sib的右孩子为红色,即删除情形为x为左孩子,黑色;sib为右孩子,黑色;sib的右孩子为红色;此时对应的是六的情形。
//需要将sib的颜色和变为其父结点的颜色,而x.parent变为黑色,sib的右孩子变为黑色,最后在对x.parent进行左旋,达到平衡。
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { //x为右孩子时
Entry<K,V> sib = leftOf(parentOf(x)); //sib为x的兄弟节点
if (colorOf(sib) == RED) {
//sib为红色时,即删除的情形为x为有孩子,黑色,sib为左孩子,红色,此时为对应上文中五的情形,需要将sib变黑, x.parent变红,再对x.parent右旋,
//这样,x的兄弟结点由sib变为sib的左孩子,且其为黑色,这样x的删除情形就变了。
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&colorOf(leftOf(sib)) == BLACK) {
//sib的左右孩子均为黑色,即删除情形为x为右孩子,黑色;sib为左孩子,黑色 ;sib的左右孩子均为黑色;此时对应的是十或十一的情形。
//需要将sib变为红色,在对x的父结点进行自平衡判断,若x.parent为红色,则只需变黑即可,若为黑色,则将再次进行平衡操作
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
//sib的左孩子为黑色,即删除情形为x为右孩子,黑色;sib为左孩子,黑色;sib的左孩子为黑色,右孩子为红色;此时对应的是九的情形。
//需要将sib变为红色,sib的右孩子变为黑色,再对sib进行左旋。此时x的兄弟节点有sib变为sib原本的左孩子,删除情形也变为七
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
//sib的左孩子为红色,即删除情形为x为右孩子,黑色;sib为左孩子,黑色;sib的左孩子为红色;此时对应的是七的情形。
//需要将sib的颜色和变为其父结点的颜色,而x.parent变为黑色,sib的左孩子变为黑色,最后在对x.parent进行左旋,达到平衡。
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK); //根结点永远为黑色
}
以上就是对Java中TreeMap的增加及删除操作源码的解读,可以看到,红黑树虽然将查找的时间复杂度降低了,但这是以复杂的自平衡操作为代价的。即代码的复杂度提升,不易于理解。
来源:oschina
链接:https://my.oschina.net/u/4085994/blog/3018238