二叉排序树在某些情况下层级过深,所以在查找的时候效率不好。
二叉平衡树在二叉排序树的基础上进行了优化,两边的平衡因子最大不能超过1,在查找的时候效率比较好,但是在删除节点的时候就会非常糟糕,因为每次删除节点可能会伴随着整棵树的移动(如:左平衡操作,右平衡操作,平衡因子修改)。
红黑树在二叉平衡树的基础上又进行了改进,它的左右子树的层次最多不超过一倍,删除节点时只需要找到此节点的后继节点,把后继节点的值赋值到要删除的节点,然后把后继节点删除,这样就省去了许多繁琐的平衡操作。
红黑树定义:它或者是一颗空树,或者是有以下性质的一棵二叉查找树:
1.节点非红即黑。
2.根节点是黑色。
3.所有NULL节点称为叶子节点,且默认颜色为黑色。
4.所有红结点的子节点必须为黑色。
5.从任意一节点到其叶子节点的所有路径上都包含相同个数的黑节点(如下图,假如从根节点出发,那么到所有 null节点的路径上都有相同数量的黑节点)。
如图:
满足以上五个条件就是红黑树。
节点插入:先按照二叉排序树的方式插入一个节点,再查找最小不平衡子树,以最小不平衡子树进行下面的操作:
1.插入的是根节点(原本树中没有节点):那么就直接将节点涂黑。
2.插入节点的父节点是黑色:如果满足以上五个条件就不需要调整。
3.插入节点的父节点是红色:
1)父节点是爷爷节点的左孩子:
情况1:爷爷节点的另一个子节点(叔叔节点)是红色,对策: 将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把 当前节点指向祖父节点,从新的当前节点开始算法
情况2:叔叔节点是黑色:
情况2.1:当前节点是其父节点的右孩子,对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左 旋。
情况2.2:当前节点是其父节点的左孩子,对策:父节点变为黑色,祖父节点变红色,再祖父节点为支点进行右 旋。
2)父节点是爷爷节点的右孩子:和上面情况一样,将左全部变成右即可
删除节点:先进行二叉排序树的删除操作,然后已替换节点作为当前节点进行后面的平衡操作
1. 当前节点是红色 :对策:直接把当前节点染成黑色,结束。
2. 当前节点x是黑色:
1)被删除节点是父节点的左孩子
(1)当前节点是根节点,对策:什么都不做。
(2)当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑),对策:把父节点染成红色,兄弟节点染成黑 色,对父节点进行左旋,重新设置x的兄弟节点。
(3)当前节点x 的兄弟节点是黑色:
-- 兄弟节点的两个孩子都是黑色,对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点。
-- 兄弟的右孩子是黑色,左孩子是红色,对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟 节点右旋,右旋后,重新设置x的兄弟节点。
-- 兄弟节点的右孩子是红色,对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右 孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算。
1)被删除节点是父节点的右孩子
对策: 把上面的左设置为右
就是这么一套逻辑,根据不同的情况进行不同的处理就行了。
/** * 红黑树 */ public class BlackRedTree<K, V> { private static final boolean RED = false; private static final boolean BLACK = true; private int modCount, size = 0; //根节点 BlackRedTreeNode<K, V> rootNode; public V deleteNode(K key) { //查找到需要删除的结点 BlackRedTreeNode<K, V> p = getEntry(key); if (p == null) return null; //获取需要删除结点的值 V oldValue = p.value; //删除结点逻辑 deleteEntry(p); return oldValue; } private void deleteEntry(BlackRedTreeNode<K, V> p) { modCount++; size--; //如果需要删除的结点既有左子树又有右子树 if (p.leftNode != null && p.rightNode != null) { //获取到该结点的后继结点(即比该结点大一位的结点) BlackRedTreeNode<K, V> s = successor(p); //把后继的值赋值给当前结点 p.key = s.key; p.value = s.value; //把p指向后继结点的位置 p = s; } //如果要删除的结点只有左孩子或者只有右孩子(如果左右孩子都有,也会执行这里删除p结点) BlackRedTreeNode<K, V> replacement = (p.leftNode != null ? p.leftNode : p.rightNode); //replacement为空说明后继结点为叶子结点或者根结点(没有左子树也没有右子树) //replacement不为空,说明replacement有左孩子或者右孩子 if (replacement != null) { //把p结点的孩子的父节点指向p的父节点 replacement.parentNode = p.parentNode; //如果p的父节点为空,说明p就是根节点,那么直接把p的孩子赋值为根节点 if (p.parentNode == null) rootNode = replacement; else if (p == p.parentNode.leftNode) //如果p是父节点的左孩子,那么就把p的孩子的父节点指向p的父节点的左孩子 p.parentNode.leftNode = replacement; else //如果p是父节点的右孩子,那么就把p的孩子的父节点指向p的父节点的右孩子 p.parentNode.rightNode = replacement; //断开p的所有引用,删除p结点 p.leftNode = p.rightNode = p.parentNode = null; //如果被插入的结点为黑色 if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parentNode == null) { // 如果被删除的是根节点 rootNode = null; } else { // 如果被删除的是叶子结点 if (p.color == BLACK) fixAfterDeletion(p); if (p.parentNode != null) { //如果是父节点的左孩子 if (p == p.parentNode.leftNode) p.parentNode.leftNode = null; else if (p == p.parentNode.rightNode) //如果是父节点的右孩子 p.parentNode.rightNode = null; //断开对p的引用 p.parentNode = null; } } } /** * From CLR */ private void fixAfterDeletion(BlackRedTreeNode<K, V> x) { //当x不是根结点并且x的颜色为黑色 while (x != rootNode && x.color == BLACK) { //如果被删除结点是父节点的左孩子 if (x == x.parentNode.leftNode) { //获取到x父节点的右孩子(x的兄弟结点) BlackRedTreeNode<K, V> sib = x.parentNode.rightNode; //如果x的兄弟结点为红色 if (sib.color == RED) { //把X的兄弟结点变为黑色 sib.color = BLACK; //把x的父亲变为红色 x.parentNode.color = RED; //进行一次左旋转 rotateLeft(x.parentNode); //把x的兄弟结点重新赋值(即赋值为旋转后的x的兄弟结点) sib = x.parentNode.rightNode; } //如果x的兄弟结点的左右孩子都为黑色,那就把兄弟結點设置为红色,并把x指针指向x的父亲 //对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点 if (sib.leftNode.color == BLACK && sib.rightNode.color == BLACK) { sib.color = RED; x = x.parentNode; } else { //如果x的兄弟结点的右孩子为黑色 //对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点右旋,右旋后,重新设置x的兄弟节点。 if (sib.rightNode.color == BLACK) { //将x兄弟节点的左孩子设为黑色 sib.leftNode.color = BLACK; //将x兄弟节点设置红色 sib.color = RED; //右旋后 rotateRight(sib); //重新设置x的兄弟节点 sib = x.parentNode.rightNode; } //兄弟节点的右孩子是红色 //对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算 sib.color = x.parentNode.color; //把当前节点父节点染成黑色 x.parentNode.color = BLACK; //兄弟节点右孩子染成黑色 sib.rightNode.color = BLACK; //再以当前节点的父节点为支点进行左旋 rotateLeft(x.parentNode); x = rootNode; } } else { // 如果被删除结点是父节点的右孩子 //注释同上, 把上面的左设置为右即可 BlackRedTreeNode<K, V> sib = x.parentNode.leftNode; if (sib.color == RED) { sib.color = BLACK; x.parentNode.color = RED; rotateRight(x.parentNode); sib = x.parentNode.leftNode; } if (sib.rightNode.color == BLACK && sib.leftNode.color == BLACK) { sib.color = RED; x = x.parentNode; } else { if (sib.leftNode.color == BLACK) { sib.rightNode.color = BLACK; sib.color = RED; rotateLeft(sib); sib = x.parentNode.leftNode; } sib.color = x.parentNode.color; x.parentNode.color = BLACK; sib.leftNode.color = BLACK; rotateRight(x.parentNode); x = rootNode; } } } x.color = BLACK; } /** * 查找结点的后继结点 * * @param t * @param <K> * @param <V> * @return */ public <K, V> BlackRedTreeNode<K, V> successor(BlackRedTreeNode<K, V> t) { if (t == null) return null; else if (t.rightNode != null) { BlackRedTreeNode<K, V> p = t.rightNode; while (p.leftNode != null) p = p.leftNode; return p; } else { BlackRedTreeNode<K, V> p = t.parentNode; BlackRedTreeNode<K, V> ch = t; while (p != null && ch == p.rightNode) { ch = p; p = p.parentNode; } return p; } } /** * 查找到需要删除的结点 * * @param key * @return */ final BlackRedTreeNode<K, V> getEntry(Object key) { if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; BlackRedTreeNode<K, V> p = rootNode; while (p != null) { int cmp = k.compareTo(p.key); if (cmp < 0) p = p.leftNode; else if (cmp > 0) p = p.rightNode; else return p; } return null; } /** * 新增结点 * * @param key * @param value * @return */ public V put(K key, V value) { BlackRedTreeNode<K, V> t = rootNode; if (t == null) { //情况1: 空树,直接添加(结点默认为黑色) rootNode = new BlackRedTreeNode(key, value, null); size = 1; modCount++; } else { //情况2:不是空树 //开始寻找要插入的位置 int cmp = 0; BlackRedTreeNode<K, V> parent; Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.leftNode; else if (cmp > 0) t = t.rightNode; else return t.setValue(value); } while (t != null); //找到要插入的位置t BlackRedTreeNode<K, V> newNode = new BlackRedTreeNode<>(key, value, parent); if (cmp < 0) {//放在parent的左孩子 parent.leftNode = newNode; } if (cmp > 0) {//放在parent的右孩子 parent.rightNode = newNode; } //插入数据完毕,开始调整树的平衡 fixInsertion(newNode); size++; modCount++; } return null; } /** * 调整红黑以及平衡度 * * @param x */ public void fixInsertion(BlackRedTreeNode<K, V> x) { //新加的节点必须为红色 x.color = RED; //如果插入结点的父节点为红色 while (x != null && x != rootNode && x.parentNode.color == RED) { //如果插入结点的父节点是它爷爷结点的左孩子 if (x.parentNode == x.parentNode.parentNode.leftNode) { //获取到它的叔叔结点 BlackRedTreeNode<K, V> y = x.parentNode.parentNode.rightNode; //如果叔叔结点为红色 if (y != null && y.color == RED) { //把它的父亲和叔叔变黑,把它的爷爷变红 x.parentNode.color = BLACK; y.color = BLACK; y.parentNode.color = RED; //把指针指向爷爷结点 x = x.parentNode.parentNode; } else {//如果叔叔结点为黑色 if (x == x.parentNode.rightNode) {//如果当前结点是其父节点的右孩子 //把指针指向它的父亲 x = x.parentNode; //进行一次左旋转 rotateLeft(x); } //把它的父亲变黑,把它的爷爷变红 x.parentNode.color = BLACK; x.parentNode.parentNode.color = RED; //进行一次右旋转 rotateRight(x); } } else { //如果插入结点的父节点是它爷爷结点的右孩子------------------------------------------------------- //获取到爷爷结点的左孩子(左叔叔结点) BlackRedTreeNode<K, V> y = x.parentNode.parentNode.leftNode; //如果叔叔结点为红色 if (y.color == RED) { //把父亲和叔叔结点变为黑色,把爷爷结点变为红色 x.parentNode.color = BLACK; y.color = BLACK; y.parentNode.color = RED; x = y.parentNode; } else {//如果叔叔结点为黑色 if (x == x.parentNode.leftNode) {//如果当前结点是其父节点的左孩子 //把指针指向它的父亲 x = x.parentNode; //进行一次右旋转 rotateRight(x); } //把它的父亲变黑,把它的爷爷变红 x.parentNode.color = BLACK; x.parentNode.parentNode.color = RED; //进行一次右旋转 rotateLeft(x); } } } //把根结点变黑色 rootNode.color = BLACK; } /** * 右旋转 * * @param p */ private void rotateRight(BlackRedTreeNode<K, V> p) { if (p != null) { BlackRedTreeNode<K, V> l = p.leftNode; p.leftNode = l.rightNode; if (l.rightNode != null) l.rightNode.parentNode = p; l.parentNode = p.parentNode; if (p.parentNode == null) rootNode = l; else if (p.parentNode.leftNode == p) p.parentNode.rightNode = l; else p.parentNode.leftNode = l; l.rightNode = p; p.parentNode = l; } } /** * 左旋转 * * @param p */ private void rotateLeft(BlackRedTreeNode<K, V> p) { if (p != null) { BlackRedTreeNode<K, V> r = p.rightNode; p.rightNode = r.leftNode; if (r.leftNode != null) r.leftNode.parentNode = p; r.parentNode = p.parentNode; if (p.parentNode == null) rootNode = r; else if (p.parentNode.leftNode == p) p.parentNode.leftNode = r; else p.parentNode.rightNode = r; r.leftNode = p; p.parentNode = r; } } /** * 节点对象 * * @param <K> * @param <V> */ public class BlackRedTreeNode<K, V> implements Map.Entry<K, V> { K key; V value; BlackRedTreeNode<K, V> leftNode; BlackRedTreeNode<K, V> rightNode; BlackRedTreeNode<K, V> parentNode; boolean color = BLACK; public BlackRedTreeNode(K k, V v, BlackRedTreeNode<K, V> parentNode) { this.key = k; this.value = v; this.parentNode = parentNode; } @Override public K getKey() { return null; } @Override public V getValue() { return null; } @Override public V setValue(V value) { return null; } } }