TreeMap底层数据结构说明
- 对于数据完全随机的场景可以使用红黑树,只有极端情况会出现退化链表和不平衡树。
- 查询性能最佳的是平衡二叉树
- 红黑树的性能综合起来是最佳的
- HashMap在java1.8以后引入了红黑树,TreeMap和TreeSet底层也是红黑树
红黑树案例
public class RBT <E extends Comparable<E>>{
private static final boolean RED = true;
private static final boolean BLACK = false;
public Node root;
private int size;
private int size(){
return size;
}
public void addEle(E e){
root = addEle(root,e);
}
public Node addEle(Node node,E e){
//第一次传入Node
if(node==null){
size++;
return new Node(e);
}
// 第一次以后比较node和新添加的node大小 左小右大
if(e.compareTo(node.e)<0){
node.left = addEle(node.left,e);
}else{
node.right = addEle(node.right,e);
}
//如果节点的右子节点是红色 且左子节点不是红色
if(isRed(node.right)&&!isRed(node.left)){
node = leftRotate(node);
}
//如果节点的左子节点是红色 且左子节点的左子节点也是红色
if(isRed(node.left)&&isRed(node.left.left)){
node = rightRotate(node);
}
//颜色翻转
if(isRed(node.right)&&isRed(node.left)){
flipColors(node);
}
return node;
}
private void flipColors(Node node) {
node.color = RED;
node.left.color=BLACK;
node.right.color = BLACK;
}
private Node rightRotate(Node node) {
Node x = node.left;
node.left = x.right;
x.right = node;
x.color = node.color;
node.color = RED;
return x;
}
private Node leftRotate(Node node) {
Node x = node.right; //x为目标失衡节点
node.right = x.left; // 将失衡节点的左节点摘除 接到失衡节点父节点的右边(左小右大)
x.left = node; // 将node节点作为失衡节点的左节点
//互换节点颜色,其实就是node节点和x节点父子关系发生了互换
x.color = node.color;
node.color=RED;
return x;
}
private boolean isRed(Node node) {
return node==null?BLACK:node.color;
}
private class Node {
public E e;
public Node left;
public Node right;
public boolean color;
public Node(E e) {
this.e = e;
this.left = null;
this.right = null;
this.color = RED;
}
@Override
public String toString() {
return "Node{" +
"e=" + e +
", left=" + left +
", right=" + right +
", color=" + color +
'}';
}
}
}
使用while循环实现层次遍历树
public void LevelTraversal(){
if(root==null){
return;
}
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
Node curNode = queue.remove(); // 调用的是 LinkedList的remove方法 删除第一个节点
System.out.println(curNode);
//判断子节点是否为null 子节点不为null就将它放入到队列中
if(curNode.left!=null){
queue.add(curNode.left);
}
if(curNode.right!=null){
queue.add(curNode.right);
}
}
}
TreeMap源码 —— 验证底层是红黑树
TreeMap中有个静态内部类 Entry
static final class Entry<K,V> implements Map.Entry<K,V> { //Entry中存储的是key value 键值对
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;
}
/**
* Returns the key.
*
* @return the key
*/
public K getKey() {
return key;
}
/**
* Returns the value associated with the key.
*
* @return the value associated with the key
*/
public V getValue() {
return value;
}
/**
* Replaces the value currently associated with the key with the given
* value.
*
* @return the value associated with the key before this method was
* called
*/
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
public String toString() {
return key + "=" + value;
}
}
TreeMap的get方法
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
// treeMap创建时是否创建了比较器
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//为了让key课比较转为Comparable
Entry<K,V> p = root; // 先让P等于根节点
while (p != null) {
int cmp = k.compareTo(p.key); //根节点的key和当前key比较 左小右大
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
TreeMap的put方法
源码中用循环实现了递归,可以避免递归容易产生的栈溢出,这一点还是值得借鉴的
public V put(K key, V value) {
Entry<K,V> t = root; //先拿到根节点
if (t == null) { //根节点为null 首次添加
compare(key, key);
root = new Entry<>(key, value, null); //创建一个新的节点
size = 1; //节点个数
modCount++; //计数器加1
return null;
}
int cmp;
Entry<K,V> parent;
//以下代码都是做出比较之后 在左子树或右子树的尾节点添加节点
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();
@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;
}
插入节点后维持平衡
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
//循环条件:新增节点不是null & 新增节点不是根节点 & 新增节点父节点是红色
while (x != null && x != root && x.parent.color == RED) {
//新增节点的父节点 是其父节点的上级节点的左孩子
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//得到父节点的上级节点的右孩子
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
//其实这里完成了颜色翻转,如果y是红色 此时 x和y都是红色
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);//因此xy应该设置为黑色
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);//x和y的上级节点设置为红色
x = parentOf(parentOf(x));
} else { //否则新增节点的上级节点的右孩子是黑色 就是x的父节点是红色 x的叔伯节点是黑色
//如果节点添加在x父节点的右边 即 右不平衡 那么进行左旋
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
//让x父节点变为黑色
setColor(parentOf(x), BLACK);
//让x父节点的上级节点变为红色
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;
}