23树与左倾红黑树
23树(23T):
结构
1.树的一个结点可以容纳2个元素,记为first,second
2.树的一个节点可以有3个儿子,记为left,middle,right
3.left的所有元素<first,first<middle的所有元素<second,second<right的所有元素
性质
1.当一个结点有k个元素时,其有且仅有k+1个儿子
2.每个叶子节点的高度相同
树的构建
insert
只在叶子节点插入,设待插入元素为val,当前遍历节点为u,则
val < first -> insert(left)
first < val < second -> insert(middle)
second < val -> insert(right)
直到下降到叶子结点
若叶子节点的元素未满2个,则直接将val插入该叶子节点
若叶子节点的元素已满2个,则将该节点的first抬升插入其父结点,val替代原本的first,若父结点也已满,则对父结点递归抬升替代操作,直到递归至元素个数未满的父节点,或者产生新的父节点
delete
和BST的删除思路一样,要删除某个节点u的值key,我们找到其右子树最小的值min所在节点v,并将min与key交换,从而变为删除叶子结点v的值key。
删除双元素叶子节点:
删除其对应元素即可
删除单元素叶子节点:
我们令待删除节点变为空节点u,我们的目的是给u填上合适的元素,进而维持23树的结构(防止子树个数与父亲元素个数不匹配规则)
下面讨论的空节点u不一定是叶子结点(因为递归删除,空节点有可能会上升的原因)
遵循原则:优先拉下父亲元素,再合并兄弟元素
1.父亲p单双元素均可,直接兄弟sib为双元素
p.first拉下给u.first,sib.first上升给p.first
如果有子树的话:sib.left交换给u.right
2.父亲p为双元素,直接兄弟sib为单元素
p.first拉下给u.first,u与sib合并
3.父亲p单元素,兄弟sib为单元素
p.first下降给u.first, u和sib合并为双元素结点new,并新建空节点X替代原本的p结点,对p结点递归填充,若X递归至新的根,删除X
左倾红黑树(LLRB)
由于23树的实现过于复杂(树的结点种类较多,树的删除操作情况太多),我们将23树优化为左倾红黑树
指的注意的是,23T与LLRB这两个集合是双射的关系,任意的LLRB对应着唯一的23T,任意的23T也对应着唯一的LLRB
结构
我们规定LLRB中一个结点只能容纳一个元素
对于23T中位于同一结点的两个元素first和second,我们使first成为second的左儿子,并使用红边连接,称first为红儿子。则红边连接的两个结点等价于23T中具有双元素的节点。而红儿子进一步地左儿子和右儿子分别为23T中的left和middle,second的右儿子即为23T中的right,按照这种拆分方式,即可将23T映射为LLRB,反之亦然
性质
LLRB的树高不超过其对应23T的树高的2倍
不存在一个结点连接着2条红边
构建
rotate(x)
左单旋:x旋转到x的左儿子的位置,x原本的右儿子旋转到x原本的位置,x原本的右儿子的左子树成为x的右子树
右单旋:x旋转到x的右儿子的位置,x原本的左儿子旋转到x原本的位置,x原本的左儿子的右子树成为x的左子树
colorflip
翻转连接的所有边的颜色,对于拥有红父亲两个黑儿子的节点而言,相当于23T中的拉下元素,使之暂时出现三元素节点;对于拥有黑父亲两个红儿子的节点而言,相当于上升元素,使三元素节点变成双元素节点。
insert
和普通BST一样,递归到叶子结点插入,且默认插入节点为红儿子,插入后:
balance
若为右红儿子,对其父亲进行左单旋
若存在连续两条红边,对第一条红边连接的父亲进行右单旋,转化为左右两个红儿子的情况
若左右两个均为红儿子,对父亲连接的所有边颜色翻转(等价于23T中的元素上升),再向上递归
delete
和BST删除思路一样,我们首先将待删除值的节点与其右子树的最小值节点交换,变为删除叶子结点,因此,我们只需实现删除树中最小值的方法即可
如何删除最小值能够尽量不改变我们的树高呢?
我们观察23T的情况,会发现树高不改变的情况只有
1.当删除值位于双元素结点
2.其父结点是双元素结点的时候
3.其直接兄弟是双元素节点时
对应到LLRB中,即从根到叶子的路径的黑链数量不改变,则对应上面所说的情况,即待删除节点为红节点,或其父节点为红节点(第三种情况不考虑),为了营造这种情况,我们采用非常暴力的方式,即如果左搜索路径上遇到了连续的两条黑链,则将其中一条翻转颜色,即对应在23T中将链其所对应的子节点元素暂时抬升,到后面再处理下拉。
这样的操作,在删除完成后,会引入如下问题:
1.出现单独的右红儿子
2.出现连续的左红儿子
3.出现左右红儿子
4.出现连续的先右后左儿子
对于前3种情况,可以在删除操作完成后,对当前u调用insert对应的balance函数使之合法,而对于第4种情况,其只能进入右子树分支处理,但由于我们删除的是最小值,因此不会进入右子树,所以我们只能在翻转过程中特判这种情况,并立马处理:
先使后左红儿子右旋,使情况成为连续右红儿子,之后再将h左旋,创建左右儿子均红的情况,最后再反转
删除最大值的情况讨论类似
具体实现代码如下
import java.util.LinkedList;public class LLRedBlackTree<Key extends Comparable<Key>, Value> {private static final boolean RED = true;private static final boolean BLACK = false;private class Node {private Key key;private Value val;private Node left, right;private boolean color;private int size;private Node(Key k, Value v, boolean c, int s) {key = k;val = v;color = c;size = s;left = right = null;}}private Node root;public LLRedBlackTree() {root = null;}public void preOrder() { //前序遍历preOrder(root, 0);System.out.print("\n");}private void preOrder(Node u, int depth) {if (u == null) {return;}preOrder(u.left, depth + 1);System.out.println(u.val + " " + depth);preOrder(u.right, depth + 1);}public void BFS() { //层序遍历if (isEmpty()) {return;}LinkedList<Node> ll = new LinkedList<>();ll.add(root);int length = 1;while (!ll.isEmpty()) {for (int i = 0; i < length; i++) {Node tmp = ll.poll();String color = tmp.color ? "Red" : "Black";System.out.print("(" + tmp.val + " " + color + " size:" + tmp.size + ") ");if (tmp.left != null) {ll.add(tmp.left);if (tmp.right != null) {ll.add(tmp.right);}}}System.out.print("\n");length = ll.size();}}private boolean isRed(Node u) {if (u == null) {return false;}return u.color == RED;}private int size(Node u) {if (u == null) {return 0;}return u.size;}private Node rotateLeft(Node u) { //保证u.right为红儿子if (u == null) {return null;}Node r = u.right;u.right = r.left;r.left = u;r.color = u.color;u.color = RED;u.size = size(u.left) + size(u.right) + 1;return r;}private Node rotateRight(Node u) { //保证u.left为红儿子if (u == null) {return null;}Node l = u.left;u.left = l.right;l.right = u;l.color = u.color;u.color = RED;u.size = size(u.left) + size(u.right) + 1;return l;}private void flipColor(Node u) { //保证u不为空,且具有左右子树u.color = !u.color;u.left.color = !u.left.color;u.right.color = !u.right.color;}private Node balance(Node u) { //保证u不为nullif (isRed(u.right) && !isRed(u.left)) { //单右红儿子u = rotateLeft(u);}if (isRed(u.left) && isRed(u.left.left)) { //连续左红儿子u = rotateRight(u);}if (isRed(u.left) && isRed(u.right)) { //左右红儿子flipColor(u);}u.size = size(u.left) + size(u.right) + 1; //有可能上面三个分支都未进行,因此需要在这里再更新一次u.sizereturn u;}private Node moveRedLeft(Node u) { //保证u连续两个左儿子都为黑儿子flipColor(u); //左儿子为黑时,右儿子必存在且为黑if (isRed(u.right.left)) { //保证右儿子存在, 特判右左儿子连续红的情况并处理u.right = rotateRight(u.right);u = rotateLeft(u);flipColor(u);}return u;}private Node moveRedRight(Node u) { //保证u的右儿子及右儿子的左儿子都为黑儿子flipColor(u);if (isRed(u.left.left)) { //特判连续左儿子红的情况,因为在调用moveRedRight之后,我们只会进入右子树,所以需要马上处理左子树的不合法情况u = rotateRight(u);flipColor(u);}return u;}public int size() {return size(root);}public boolean isEmpty() {return root == null;}public void insert(Key k, Value v) {if (k == null) {return;}if (v == null) {delete(k);return;}if (root == null) {root = new Node(k, v, BLACK, 1);}root = insert(root, k, v);}private Node insert(Node u, Key k, Value v) {if (u == null) {return new Node(k, v, RED, 1);}int cmp = k.compareTo(u.key);if (cmp < 0) {u.left = insert(u.left, k, v);} else if (cmp > 0) {u.right = insert(u.right, k, v);} else {u.val = v;}u = balance(u);return u;}private Node deleteMin(Node u) {if (u.left == null) { //当递归左子树时,若其左儿子不存在,则右儿子一定不存在,那么这个就是最小的叶子结点,于是删除返回nullreturn null;}if (!isRed(u.left) && !isRed(u.left.left)) { //否则其一定有左儿子,那么判断是否有连续黑左儿子u = moveRedLeft(u);}u.left = deleteMin(u.left);//递归删除左子树return balance(u);//处理不合法情况并返回}public void deleteMin() {if (isEmpty()) {return;}root = deleteMin(root);}private Node deleteMax(Node u) {if (isRed(u.left)) { //判断是否有左红儿子,有的话,右旋uu = rotateRight(u);}if (u.right == null) { //前面的条件分支保证了这个分支的合法性return null; //若进行过前面的条件操作,那么右儿子不为空,不进入该分支//若未进行过前面的条件操作,说明u有黑左右儿子,或者u没有左儿子//如果u没有左儿子,又没有右儿子,那么u为叶子节点,直接删除即可}if (!isRed(u.right) && !isRed(u.right.left)) { //这里为什么是u.right.left而不是u.right.right?u = moveRedRight(u);}u.right = deleteMax(u.right);return balance(u);}public void deleteMax() {if (isEmpty()) {return;}root = deleteMax(root);}private Node min(Node u) {if (u.left == null) { //若无左儿子,一定是叶子结点return u;} else {return min(u.left);}}public Value min() {if (isEmpty()) {return null;}return min(root).val;}private Node max(Node u) {if (u.right == null) {return u;} else {return max(u.right);}}public Value max() {if (isEmpty()) {return null;}return max(root).val;}private Node delete(Node u, Key k) {if (k.compareTo(u.key) < 0) { //递归左子树if (!isRed(u.left) && !isRed(u.left.left)) {u = moveRedLeft(u);}u.left = delete(u.left, k);} else { //k>=u.keyif (isRed(u.left)) { //若右旋了,不会进入下一个if分支u = rotateRight(u);}if (k.compareTo(u.key) == 0 && u.right == null) { //叶子结点直接删除return null;}if (!isRed(u.right) && !isRed(u.right.left)) { //摧毁连续黑链u = moveRedRight(u);}if (k.compareTo(u.key) == 0) { //找到目标,且其具有右子树Node tmp = min(u.right);u.key = tmp.key;u.val = tmp.val;u.right = deleteMin(u.right);} else { //递归右子树u.right = delete(u.right, k);}}return balance(u);}public void delete(Key k) {if (isEmpty()) {return;}root = delete(root, k);}private Node get(Node u, Key k) {if (u == null) {return null;}int cmp = k.compareTo(u.key);if (cmp < 0) {return get(u.left, k);} else if (cmp == 0) {return u;} else {return get(u.right, k);}}public Value get(Key k) {if (k == null || isEmpty()) {return null;}Node result = get(root, k);if (result == null) {return null;}return result.val;}public boolean contains(Key k) {return get(k) != null;}private int height(Node u) {if (u == null) {return -1;}return 1 + Math.max(height(u.left), height(u.right));}public int height() {return height(root);}//返回小于k的最大值——k的前驱private Node floor(Node u, Key k) {if (u == null) {return null;}if (k.compareTo(u.key) <= 0) {return floor(u.left, k);}Node result = floor(u.right, k);if (result != null) {return result;}return u;}public Value floor(Key k) {if (k == null) {return null;}Node result = floor(root, k);if (result == null) {return null;}return result.val;}//返回大于k的最小值——k的后继private Node ceiling(Node u, Key k) {if (u == null) {return null;}if (k.compareTo(u.key) >= 0) {return ceiling(u.right, k);}Node result = ceiling(u.left, k);if (result != null) {return result;}return u;}public Value ceiling(Key k) {if (k == null) {return null;}Node result = ceiling(root, k);if (result == null) {return null;}return result.val;}//返回k的排名private int rank(Node u, Key k) {int cmp = k.compareTo(u.key);if (cmp < 0) {return rank(u.left, k);} else if (cmp == 0) {return 0;} else {return size(u.left) + 1 + rank(u.right, k);}}public int rank(Key k) {if (!contains(k)) {return -1;}return rank(root, k);}private Node getRank(Node u, int rank) {if (rank < size(u.left)) {return getRank(u.left, rank);} else if (rank == size(u.left)) {return u;} else {return getRank(u.right, rank - size(u.left) - 1);}}//返回排名为rank的结点的值public Value getRank(int rank) {if (rank < 0 || rank >= size()) {return null;}return getRank(root, rank).val;}
}