Java_JDK_TreeMap
(一)TreeMap
TreeMap使用的是红黑树来实现的,所以重点是红黑树的插入和删除。
红黑树的3个特性:
- 根节点和所有外部节点的颜色都是黑色的;
- 从根节点到外部节点的途中没有连续两个节点的颜色是红色;
- 所有从根节点到外部节点的路径上都有相同数目的黑色节点。
java中TreeMap的节点结构:Entry结构,是TreeMap的一个内部类
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK; /**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
}
下面来分析插入操作:
要注意的是:
- 要插入的节点总是红色的;
插入共分种情况:
- 插入的是根节点,则直接插入即可,然后将根节点设置为黑色;
- 插入的是非根节点,则要看找到应该插入的位置进行插入即可。
java中TreeMap实现插入算法主要分为两个函数:put(K key, V value), 和fixAfterInsertion(Entry<K, V> x).
(1)put(K key, V value):
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); //t即为要插入的位置
} while (t != null);
}
else { //使用默认的排序算法
if (key == null)
throw new NullPointerException();
@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);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e); //插入后进行红黑颜色的调整
size++;
modCount++;
return null;
}
(2)fixAfterInsertion(Entry<K, V> x)函数:
其中,使用了红黑树的rotateLeft 和rotateRight方法:
/** From CLR */
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.parent.left == p)
p.parent.left = r;
else
p.parent.right = r;
r.left = p;
p.parent = r;
}
} /** From CLR */
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.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
l.right = p;
p.parent = l;
}
}
下面来分析删除操作:
删除其实是非常麻烦的一个操作,但是这里Java并不是直接删除节点,而是找到一个最方便删除的节点替换至要删除的节点,然后删除即可。
什么意思呢?
简而言之,删除操作共分三种情况:
- 要删除的节点p是根叶子节点:
- 根据特性1,我们知道所有外部节点都是黑色的,故p一定是黑色的。但是这样冒然删去p一定会导致某条路径上少一个黑色节点,所以需要进行红黑调整;
- 要删除的节点p的左孩子或者右孩子中有一个是空的:
- 这种情况也很简单,直接修改指针删除p就可以了;
- 同样,如果删除的p是黑色的也要进行红黑调整;
- 要删除的节点p的左孩子和右孩子都不为空:
- 这时需要找到p的前驱,即左孩子的最右边或者右孩子的最左边;
- 然后将找到的前驱节点替换p,然后删除其前驱节点就OK啦~
在java中,删除主要分两个部分,一个是找到要删除的节点,是由deleteEntry(Entry<K, V> p)函数实现的;一个是调整颜色,是由fixAfterDeletion(Entry<K, V> x)实现的。
下面分别进行分析:
(1)deleteEntry函数:这里就是将上面三种情况考虑好,然后找到要删除的节点进行删除,中间调用了fixAfterDeletion函数。
/**
* Delete node p, and then rebalance the tree.
*/
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--; // If strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) { //如果左右子树都不为空,则找到p的前驱(右子树的最左节点或者左子树的最右节点)s,然后用s来代替p,p指向s,则最终删除s即可。
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children // Start fixup at replacement node, if it exists.
//如果上一个if不满足,则说明至少有一个子树时空的,另replacement指向不空的那个子树的根节点,直接删除p即可
Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null; // Fix replacement,删除p之后,需要保持红黑树的特性,所以要调节他们的颜色
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else { // No children. Use self as phantom replacement and unlink.
// p为叶子节点,可以直接删除,但如果是黑色节点需要调整
if (p.color == BLACK)
fixAfterDeletion(p); if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
(2)fixAfterDeletion()函数:
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x)); if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
} if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x)); if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
} if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
} setColor(x, BLACK);
}
具体的分析见下图:
还有一种对称的情况,不再额外进行分析。
完整的fixAfterDeletion如下:
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x)); if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
} if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x)); if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
} if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
} setColor(x, BLACK);
}
Java_JDK_TreeMap的更多相关文章
随机推荐
- SQL分组取每组前一(或几)条记录(排名)
mysql分组取每组前几条记录(排名) 附group by与order by的研究 http://www.jb51.net/article/31590.htm --按某一字段分组取最大(小)值所在行的 ...
- 帮初学者改代码——playerc之“练习:求完数问题”(上)
原文:“练习:求完数问题” 原代码: // #include <stdio.h> #include <stdlib.h> #include <math.h> #de ...
- Java图像文件的读写
读取bmp文件到BufferedImage中File file2 = new File("c:\\testimages\\tttt" + ".bmp");// ...
- SQL2005中的事务与锁定(二)- 转载
------------------------------------------------------------------------ -- Author : HappyFlyStone ...
- ApkDec android反编译工具
转自:http://www.newasp.net/soft/70498.html 下载 ApkDec是一款免费的绿色APK反编译工具 forandroid ,由android开发者社区开发. ApkD ...
- selenium滚动条
element = self.brower.find_element_by_id('xxx')brower.execute_script('arguments[0].scrollIntoView(); ...
- Delphi XE的firemonkey获取当前文件所在路径的方法
Delphi XE的firemonkey获取当前文件所在路径的方法 在之前,我们知道有三种方法: ExtractFilePath(ParamStr(0)) ExtractFilePath(Applic ...
- [转]学习 WCF (6)--学习调用WCF服务的各种方法
转自:http://www.cnblogs.com/gaoweipeng/archive/2009/07/26/1528263.html 作者这篇博文写得很全面. 根据不同的情况,我们可以用不同的方法 ...
- php如何支持实现多线程并发
<?php if(function_exists('date_default_timezone_set')) { date_default_timezone_set('PRC'); } func ...
- Codeforces 733C:Epidemic in Monstropolis(暴力贪心)
http://codeforces.com/problemset/problem/733/C 题意:给出一个序列的怪兽体积 ai,怪兽只能吃相邻的怪兽,并且只有体积严格大于相邻的怪兽才能吃,吃完之后, ...