红黑树是一种平衡的二叉树,其在二分搜索树的基础上添加了一些特性,保证其不会退化成链表。java
说明:以上红黑树的定义看完很快就会忘记,疑惑点在于为何要这么定义。为何有的节点是红色或是黑色。为何这么几条限制就能保证红黑树的平衡性。这些疑问点在看完下面对于“2-3树”的介绍后会逐渐变得明朗,“2-3树”和红黑树是等价的,红黑树的各个特性能够类比于“2-3树”。node
2-3树是一颗绝对平衡的树,从根节点到任意叶子节点所应该的节点数量是相等的;它知足二分搜索树的基本性质;有的节点仅包含一个元素,它容许有2个孩子节点,被称为“2节点”;有的节点包含两个元素,它容许有三个孩子节点,被称为“3节点”。算法
说明:元素添加到2-3树的2节点直接融合成一个3节点便可。性能
说明:元素添加到2-3树的3节点会临时融合成“4节点”,而后会裂变成三个2节点,以其中一个节点为根,裂变后仍然知足二分搜索树的性质。this
说明:元素添加到2-3树的3节点会临时融合成“4节点”,而后会裂变成三个2节点,以其中一个节点为根;此临时4节点裂变后产生的新子树高度增长了1,破坏了绝对平衡性,因而这颗新子树的根须要继续向其父亲节点融合,融合后新子树增长的高度被抹平,从新保持绝对平衡。spa
说明:2-3树维护绝对平衡,靠的就是融合、裂变的操做,融合后树的高度保持变后者高度下降,而裂变操做后会增长树的高度,所以裂变后会伴随着融和操做,融合操做中和裂变操做。从而保证了2-3的绝对平衡。3d
说明:红黑树之因此定义节点的红黑颜色,实际上就是模拟2-3树的2节点或者3节点。咱们再来回顾一下关于开头的红黑树定义,下面会作个简单说明。code
说明:模拟2-3树的2节点和3节点只须要两种颜色便可,红色节点表示与其父亲节点融合。blog
说明:根节点做为红黑树的根,不须要向上融合了。继承
说明:由于一个红色节点与其父亲节点已经组成了相似于2-3树的3节点了,若是其孩子节点依然是红色的,那就构成了2-3树中的临时“4节点”,此时2-3树要进行裂变和融合操做保证绝对平衡性。那么相似于2-3树,红黑树有连续两个节点是红色的话,红黑树须要进行旋转操做(相似于2-3树的裂变操做)配合颜色翻转操做(相似于2-3树的融合操做),以使得红黑树保持平衡性。连续连续两个红色节点也是红黑树出发自平衡的触发点,因此不会出现连续两个红色节点。
说明:因为红黑树是模拟于2-3树,而2-3树是绝对平衡的树,因此除去表示融合的红色节点,红黑树中的黑色节点也是绝对平衡的,红黑树也被称为是“黑平衡”的二叉树,所以从红黑树的任意节点出发到叶子节点,所经历的黑色节点数是一致的。
红黑树在添加元素、删除元素后会影响树的平衡性,可能会破坏红黑树的定义。相似于2-3树的平衡操做(裂变节点、融合节点),红黑树也有其平衡操做,左右旋转相似于2-3树的裂变节点;颜色翻转相似于2-3树的融合节点。
说明:如下会实现一个左倾红黑树,在递归算法实现的红黑树添加操做中,当递归到深层的叶子节点融合元素(红色节点)后可能形成不平衡,须要递归逐层向上维护节点状态。
说明:左倾红黑树规定节点的右孩子不能是红色节点,须要左旋操做。
说明:左旋操做后当前子树的根节点发生了变化,当前的根节点须要继承保留原有根节点的颜色,同时原来的根节点变成红色节点。若是原有根节点是红色节点,那么上层须要继续进行相关操做。
说明:连续两个节点为红色节点,违反了红黑树的定义,相似于造成了2-3树的临时4节点,此时红黑树的平衡被破坏,须要再平衡操做。
说明:右旋操做后,发现节点状态依然相似于2-3树的临时4节点状态,须要继续裂变和融合操做,而红黑树此种场景只须要向上融合便可,即须要颜色翻转,使x节点变成红色节点表示向上融合。
总结:红黑树模拟于2-3树也等价于2-3树。红黑树的左右旋转等价于2-3树的裂变节点操做;红黑树的颜色翻转等价于2-3树的融合节点操做;不一样点在于红黑树的左右旋转会能够下降子树的高度,而2-3树的裂变节点操做会增长子树的高度;红黑树的颜色翻转操做不会改变子树的高度,是一个抽象的融合操做,而2-3树的融合操做能够下降子树高度。最终红黑树也会趋于平衡,而2-3会保持绝对平衡。
import java.util.ArrayList; public class RBTree<K extends Comparable<K>, V> { private static final boolean RED = true; private static final boolean BLACK = false; private class Node{ public K key; public V value; public Node left, right; public boolean color; public Node(K key, V value){ this.key = key; this.value = value; left = null; right = null; color = RED; } } private Node root; private int size; public RBTree(){ root = null; size = 0; } public int getSize(){ return size; } public boolean isEmpty(){ return size == 0; } /** * 判断节点是不是红色 * @param node 树节点 * @return */ private boolean isRed(Node node) { if (node == null) { return BLACK; } return node.color; } /** * 节点左旋转操做 * node x * / \ 左旋转 / \ * T1 x ---------> node T3 * / \ / \ * T2 T3 T1 T2 * @param node * @return */ private Node leftRotate(Node node) { Node x = node.right; //左旋转 node.right = x.left; x.left = node; //颜色维护 x.color = node.color; node.color = RED; return x; } /** * 右旋转操做 * node x * / \ 右旋转 / \ * x T2 -------> y node * / \ / \ * y T1 T1 T2 * @param node * @return */ 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; } /** * 颜色翻转 * @param node */ private void flipColors(Node node) { node.color = RED; node.left.color = BLACK; node.right.color = BLACK; } /** * 向红黑树中添加新的元素(key, value) * @param key 元素key * @param value 元素value */ public void add(K key, V value){ root = add(root, key, value); root.color = BLACK;//最终根节点为黑色节点 } /** * 向以node为根的二分搜索树中插入元素(key, value),递归算法 * 返回插入新节点后二分搜索树的根 * @param node 树节点 * @param key 元素key * @param value 元素value * @return 返回插入新节点后二分搜索树的根 */ private Node add(Node node, K key, V value){ if(node == null){ size ++; return new Node(key, value); } if(key.compareTo(node.key) < 0) node.left = add(node.left, key, value); else if(key.compareTo(node.key) > 0) node.right = add(node.right, key, value); else // key.compareTo(node.key) == 0 node.value = value; //左旋操做 if (isRed(node.right) && !isRed(node.left)) { node = leftRotate(node); } //右旋操做 if (isRed(node.left) && isRed(node.left.left)) { node = rightRotate(node); } //颜色翻转 if (isRed(node.left) && isRed(node.right)) { flipColors(node); } return node; } /** * 返回以node为根节点的二分搜索树中,key所在的节点 * @param node 节点 * @param key 元素key * @return */ private Node getNode(Node node, K key){ if(node == null) return null; if(key.equals(node.key)) return node; else if(key.compareTo(node.key) < 0) return getNode(node.left, key); else // if(key.compareTo(node.key) > 0) return getNode(node.right, key); } public boolean contains(K key){ return getNode(root, key) != null; } public V get(K key){ Node node = getNode(root, key); return node == null ? null : node.value; } public void set(K key, V newValue){ Node node = getNode(root, key); if(node == null) throw new IllegalArgumentException(key + " doesn't exist!"); node.value = newValue; } /** * 返回以node为根的二分搜索树的最小值所在的节点 * @param node * @return */ private Node minimum(Node node){ if(node.left == null) return node; return minimum(node.left); } }
红黑树相比于AVL树,实际上是牺牲了平衡性的,红黑树并不彻底知足平衡二叉树的定义,红黑树的最大高度达到了2logn的高度,红色节点影响了红黑树的的平衡性。红黑树虽然牺牲了必定的查询性能,可是在增删改操做的性能获得了弥补,红黑树的综合性能仍是要优于AVL树的。