红黑树详解,红黑树与AVL树区别
红黑树是一种很经典的数据结构,它可以在O(log n)时间内做查找,插入和删除。所以倍受关注。但是一直以来很多Java程序员对他都不是很重视,直到在JDK 1.8中,HashMap会将其链表转换成红黑树,此后,很多人就开始重新学习红黑树的有关知识。
作者在学习红黑树时,查阅了很多资料都没有找到解释的特别清楚的,于是自己总结了这一篇文章,总字数近7k,而且绘制了很多图,希望可以让大家更容易理解。
1. 定义
红黑树是Avl树的一个变种,它也是在二叉查找树的基础上添加平衡条件,只是它对平衡条件的描述不像AVl树那样直接,而是转化成对节点颜色规则的描述。
颜色规则:
- 对于任意节点,要么是红色,要么是黑色;
- 根节点是黑色的;
- 如果一个节点是红色的,那么它的子节点必须是黑色的(即不能有两个连续的红色节点);
- 任意节点到其下面各个空结点(后面称为nil节点,并约定其颜色为黑色)的路径上都包含相同数目的黑色节点(称为黑高);
通过对任何一条从根到空节点的路径上各个结点的颜色进行约束,红黑树可以确保没有一条路径会比其他路径长出2倍,因而红黑树是近似平衡的。
2. 分析实现
为了便于处理红黑树代码中的边界条件,使用两个标记节点header和nil来充当哨兵,它们与树中的普通节点有相同的属性,颜色设为黑色。将header的值设为负无穷,因此它的右子节点就是真正的根,在对树进行遍历时我们可以用header作为起点。nil的值可以为任意值,将它作为所有空节点的统一引用, 即让所有树叶的左右子节点都指向nil,那么在遍历时就可以将nil视为结束的标志。红黑树也是一颗查找二叉树,所以它的基本操作与查找二叉树一致,区别也在于插入和删除后的平衡恢复。只是实现平衡时的目标就是满足它对颜色规则的定义,至于树的平衡与否已经由规则本身去保证。
2.1. 插入
与二叉查找树一样,插入节点其实就是添加一个树叶,主要问题是在添加结束时可能要做一些调整来保证红黑树的规则不被破坏。不过,根据规则4, 可以知道,在插入之前任意节点到其下面各个nil的路径上都包含相同数目的黑节点数,如果能保证在插入节点前后父节点parent或者祖父节点grand到其下面nil的路径上的黑节点数不变, 即保证parent或者grand的黑高不变,那么就可以保证红黑树的规则不被破坏,这样就可以将平衡操作时需要考虑的范围缩小到grand。由于插入红色节点不会影响路径上的黑节点数,因此就先将待插入的节点涂成红色,如果parent是黑色,那么插入直接完成。如果parent是红色,那么插入之后将违反规则3, 这时再做一些调整。先分析一下插入之前的可能情形,根据规则3,如果parent是红色,那么可以断定grand以及parent的另一个子节点一定是黑色。不仅如此,因为插入的是树叶节点在之前一定是nil, 那么parent的另一个子节点必然也是nil,否则插入之前parent就违反了规则4。再看parent的兄弟节点,如果它是黑色的,那么它一定也是nil节点;如果它是红色的,那么它的两个子节点一定都是nil节点,否则插入之前grand就违反了规则4。所以,如果parent是红色,那么我们可以完整地画出插入之前的所有可能情况,一共有4种,其中待插入节点的位置就是图中parent下面的两个nil之一。可以看出,1和2,3和4分别是对称的两种情况,区别在于parent与grand的大小关系,其实可以概括为parent的兄弟节点uncle是黑色还是红色的两种情况。
1. 如果parent为红色,uncle为黑色
以情况1为例(即parent小于grand):如果current小于parent,那么插入之后,可以将parent和grand的颜色互换,然后将grand右旋,这样就能保证原来以grand为根的这个子树的黑高保持不变,也就保证了整棵树不被破坏。如果current大于parent,则可以先将parent左旋,然后将current视为parent,再与上面进行相同的操作,即可恢复平衡。
2. 如果parent为红色,uncle为红色
这时无论怎么旋转都已经解决不了问题,不过可以知道,在插入之前grand到下面各个nil的黑节点数为1,因此,只要保证grand的黑高仍然为1就行, 那么,通过将grand与其左右子节点的颜色进行翻转便可达到目标。
以情况3为例:但是,翻转会使grand变成红色,这时如果曾祖父也是红色,那么将违反了规则3。因此可能需要朝着树根的方向进行逐层分析,直到不再有两个相连的红色节点或者到达树根为止,但这显然会很麻烦。 事实上,由于颜色翻转的等价性,我们可以在查找插入节点位置的过程中,通过颜色翻转来避免掉uncle为红色的情况,即将颜色翻转的事情提前做了。在自上而下的查找过程中,如果看到一个节点X的两个子节点都为红色,可以使X变成红色,而两个子节点变成黑色(如果X是根,则可以在翻转之后再将其置为黑色),结果依然满足红黑树的规则。只有当X的parent为红色时,才会破坏红黑树的规则。不过这时可以断定X的兄弟节点,X的grand,以及X的uncle都是黑色的(因为如果X的parent和uncle都为红色, 那么一定会在自上而下的查找和翻转过程中被过滤掉)。那么,可以将X视为current,然后与上面parent为红色,uncle为黑色的情形进行相同的处理。下面通过画图来说明一种情形(对称情形类似处理):
先对X与其子节点进行颜色翻转然后,将X视为之前的current,对parent和grand进行换色和旋转
完成之后,将新的根(即原本grand的位置)视为新的current,然后重新向下遍历。由于X的孙子节点之前一定为黑色,而X的儿子节点在翻转之后也为黑色,因此在调整之后, 可以保证在X的下面将有连续两层不会出现红节点。所以,尽管旋转调整会使得gand的位置变得错误,但在接下来的遍历中,又会立即将grand-parent-current恢复成正确的位置关系。总结节点插入问题:首先根据二叉查找树的性质,可以知道插入节点就是添加树叶,其次对于红黑树而言,插入红色节点不会影响路径上的黑节点数。因此, 我们将插入的节点视为红色的树叶,这时只要考虑插入的红色树叶的parent是红色还是黑色,如果是黑色问题直接解决,如果是红色,再看uncle是红色还是黑色。如果uncle是黑色,可以通过变色旋转解决;如果是红色,那么通过颜色翻转也能达到目的,但这会使grand变成红色,需要进一步考虑曾祖父的颜色问题,将问题放大了。于是考虑在一开始向下的遍历过程中, 提前翻转颜色来避免掉parent和unlce同时为红色的情况。因此,最终在插入节点时,实际需要考虑旋转调整的情形只有一种,即parent为红色,uncle为黑色。
2.2. 删除
对于节点删除,大概思路也是先将问题转化成删除树叶来简化问题,如果是红色树叶,则可以直接删除,如果是黑色树叶,则再分情形进行讨论。先看下如何将删除目标转化成对树叶的删除,根据二叉查找树的性质,在删除节点时可以用右子树的最小节点或左子树的最大节点来替换要删除的节点,然后删除子树中用来替换的那个节点。不过在红黑树中,为了保证节点的颜色规则,替换时将只进行值的替换。其实,根据红黑树的规则可以得出一个简单的推论:如果一个树叶没有兄弟节点,那么它一定为红色,或者说如果一个树叶为黑色, 则它必然有兄弟节点。另外,还可以断定:对于任意节点,其右子树的最小(即最左)节点最多存在一个红色右子节点,其左子树的最大(即最右)节点最多存在一个红色左子节点。因此,当左子树的最大节点或右子树的最小节点不是树叶时,可以继续用同样的思路来转换要删除的目标,并且这时可以断定最后删除的树叶一定是红色。再来讨论删除黑色树叶的情形,由于黑色树叶必然会存在兄弟节点(树根除外),因此在下面的讨论中首先假设要删除的黑色树叶总是在左边(如果在右边则对称处理), 这时可以根据其兄弟节点brother和父亲节点parent的颜色来将删除情形分为3种。下面分别进行画图分析(图中省略掉空节点nil):
1.parent为红色,brother为黑色
由于current是树叶,那么可以断定brother下面不会再有黑色的后代节点,至多只能有红色的儿子节点,否则parent在删除前将违反规则4。
1.1 先看brother没有儿子的情况,此时看上去比较简单,直接对parent进行左旋即可解决问题,能够保证在删除调整前后以原parent为根的子树的黑高不变。 1.2 但是,如果brother有一个红色左儿子(右儿子没有影响,因此忽略),那么直接对parent进行左旋,将会造成两个连续的红色的节点而违反规则3, 这时需要先将brother右旋,然后再对parent进行换色并左旋来解决。
因此,需要根据brother是否有红色左儿子将情形1再分为2种子情形来进行处理。
2.parent为黑色,brother为红色
同样由于current是树叶,可以断定brother的两个黑色的儿子节点下面至多只能有一层红色子节点,下面根据brother左儿子的子节点情况(右儿子的子节点没有影响,因此忽略)来分情形讨论。
2.1 还是先看brother的左儿子没有子节点的情况,这时可以将brother与其左儿子进行换色,然后将parent进行左旋。 2.2 如果brother的左儿子有一个红色右子节点,这时需要先将brother左儿子的右子节点涂成黑色,并将brother右旋,然后再将parent左旋。
2.3 如果brother的左儿子没有红色右子节点,但有一个红色左子节点。这时可以先将brother左儿子的左子节点涂成黑色,并将brother的左儿子右旋,这样将情形转化成2.2, 然后依次将brother右旋,parent左旋。
因此,需要根据brother左儿子的子节点情况将情形2再分为3种子情形来处理。
3.parent为黑色,brother为黑色
还是由于current是树叶,可以断定brother下面至多只能有一层红色的儿子节点,否则parent在删除前就违反了规则4。
3.1 还是先看brother没有儿子的情况,此时通过parent为根的子树自身已经解决不了问题,想法是将brother变成红色来降低parent子树的黑高,然后将parent子树整个视为一个current进行处理, 下面会再详细说明。 3.2 如果brother有一个红色右儿子,这时可以将brother的右儿子变成黑色,然后将parent左旋。
3.3 如果brother没有红色右儿子,但是存在一个红色左儿子,这时可以将brother的左儿子变成黑色,然后将brother右旋,再将parent左旋。
因此,需要根据brother的子节点情况将情形3再分为3种子情形来处理。小结一下,上面一共将删除了黑色树叶的情形分成了8种,除了3.1,其它都可以在parent子树内部解决问题。为了进一步分析3.1的问题,需要扩展下思维, 可以想象一下,将current是树叶这个前提去掉,然后将current视为根为黑色的子树。假设这时删除的黑色树叶落在current子树下面, 并且遇到了情形3.1,那么可以看成将current子树整体的黑高降1,然后再根据brother子树和parent的情况来进行处理。如果依然解决不了问题, 那么可以将parent树视为新的current子树, 再将祖父节点视为新的parent,这样逐层向上分析,直到能够在新的parent为根的树内部解决问题或者到达树根为止。下面通过画图再对上面的情形进行一遍说明,对于空节点nil可以使用黑色节点代替。另外,这时有一个前提假设:即在删除之前current子树的黑高一定大于1, 那么brother子树的黑高必然也至少为2,也就是说brother到nil的路径上必然还有黑色节点。为了更好的说明,图中用x标出树根, 并用a,b,c,d,e,f,g,h标出树根下面的一些相对位置(可以将这些位置看成nil,只是图中做了省略,没有画出current和brother到这些nil的完整路径)。所有图的最终目标的都是保证:如果current子树的黑高降1,那么通过调整保证x到a,b,c,d,e,f,g,h这些位置的相对黑高不变。对于1.1,如果current子树黑高降1,与之前一样,直接将parent左旋,可以保证在调整前后,x到a,b,c,d,e的相对黑高保持不变,如图:
对于1.2,如果current子树黑高降1,与之前一样,先将brother右旋,然后将parent变成黑色并左旋,也能保证x到a,b,c,d,e的相对黑高保持不变,如图:
对于2.1,如果current子树黑高降1,与之前一样,将brother与左儿子换色,然后对parent左旋,可以保证x到a,b,c,d,e,f,g,h的相对黑高保持不变,如图:
对于2.2,如果current子树黑高降1,与之前一样,将brother的左儿子的右子节点涂成黑色,然后对brother右旋,再对parent左旋,也可以保证x到a,b,c,d,e,f,g,h的相对黑高保持不变, 如图(A涂成灰色表示其颜色可以红或者黑,没有影响):
对于2.3,如果current子树黑高降1,与之前一样,先将brother的左儿子的左子节点涂成黑色,并对将brother的左儿子右旋,这样将情形转化成2.2,然后再进行同样的旋转调整, 以保证x到a,b,c,d,e,f,g,h的相对黑高保持不变,如图:
对于3.1,如果current子树黑高降1,与之前一样,依然只能降低brother的黑高,使x到a,b,c,d,e,f的相对黑高同时降1,然后将parent视为新的current子树继续向树根追溯,如图:
对于3.2,如果current子树黑高降1,与之前一样,将brother的右儿子涂黑,然后对parent左旋,也能保证x到a,b,c,d,e,f的相对黑高保持不变, 如图(图中将left涂成灰色表示其颜色可以红或者黑,没有影响):
对于3.3,如果current子树黑高降1,与之前一样,先将brother的左儿子涂黑,然后对parent右旋,再对parent左旋,也能保证x到a,b,c,d,e,f的相对黑高保持不变,如图:
总结节点删除问题:首先将问题转化为对树叶的删除,如果树叶是红色则直接删除,如果是黑色,则根据兄弟节点和父节点的颜色情况来对情形进行分类。想法是尽量在子树中解决, 如果实在解决不了,就降低子树的高度,并将子树视为整体然后向树根进行追溯,直到问题解决。上面的分析中,对于删除黑色树叶的删除,分成了8种情形,其中3.1需要向树根追溯, 直到转化成其它7种情形或到树根为止才能解决。其次,2.3也需要转化成2.2来解决,因此,对于黑色树叶的删除,实际上最后直接解决的情形可以归结有6种。
3. java实现
继承之前二叉查找树的实现,重写下插入和删除,分别实现Set和MultiTreeMap
3.1. Set
public class RBTreeSet<E extends Comparable<? super E>> extends BinarySearchTreeSet<E> {private RBNode<E> header;private RBNode<E> nil;public RBTreeSet() {nil = new RBNode<>(null);nil.left = nil;nil.right = nil;header = new RBNode<>(null, nil, nil);}@Overrideprotected Node<E> getRoot() {return header.right;}@Overrideprotected boolean isEmptyNode(Node<E> node) {return nil == node;}@Overridepublic void clear() {header.right = nil;size = 0;}@Overridepublic boolean add(E e) {RBNode<E> current = header;RBNode<E> parent = header;//先将nil的值设为enil.element = e;//然后从header开始自上而下寻找值为e的节点awhile(compare(e, current) != 0){parent = current;if(compare(e, current) < 0){current = getLeft(current);}else{current = getRight(current);}//如果左右子节点都为红色,则进行颜色翻转,避免插入时出现父亲的兄弟节点为红色的情况if(getLeft(current).red && getRight(current).red){current = handleReorient(current);}}//如果发现了值相同的重复节点,直接覆盖if(current != nil){current.element = e;return true;}//新建一个数据节点代替nil,并将其左右子树指向nilcurrent = new RBNode<>(e, nil, nil);size++;//当while结束时,必然可以明确待插入节点的parent节点,这里使parent节点链接到新节点current.parent = parent;if(compare(e, parent) < 0){parent.left = current;}else{parent.right = current;}//将新数据节点设成红色,如果父亲节点为红色则需要旋转调整handleReorient(current);return true;}private RBNode<E> getLeft(Node<E> node){return (RBNode<E>)node.left;}private RBNode<E> getRight(Node<E> node){return (RBNode<E>)node.right;}private int compare(E e, Node<E> node) {if(node == header){return 1;}else{return e.compareTo(node.element);}}private RBNode<E> handleReorient(RBNode<E> current) {getLeft(current).red = false;getRight(current).red = false;current.red = true;RBNode<E> subRoot = current;//翻转之后发现parent也是红色,进行换色和旋转RBNode<E> parent = current.parent;if(parent.red){RBNode<E> grand = parent.parent;grand.red = true;//旋转parent, 向外旋转到同一侧if(compare(current.element, grand) != compare(current.element, parent)) {if(compare(current.element, parent) > 0){rotateLeft(parent);}else{rotateRight(parent);}}//旋转grandif(compare(current.element, grand) < 0){subRoot = getLeft(grand);subRoot.red = false;rotateRight(grand);}else{subRoot = getRight(grand);subRoot.red = false;rotateLeft(grand);}}//直接将根节点置为黑色getRight(header).red = false;return subRoot;}private void rotateRight(RBNode<E> node) {RBNode<E> parent = node.parent;RBNode<E> left = getLeft(node);RBNode<E> leftRight = getRight(left);left.right = node;node.parent = left;node.left = leftRight;leftRight.parent = node;left.parent = parent;if(parent.right == node){parent.right = left;}else{parent.left = left;}}private void rotateLeft(RBNode<E> node) {RBNode<E> parent = node.parent;RBNode<E> right = getRight(node);RBNode<E> rightLeft = getLeft(right);right.left = node;node.parent = right;node.right = rightLeft;rightLeft.parent = node;right.parent = parent;if(parent.right == node){parent.right = right;}else{parent.left = right;}}@Overridepublic boolean remove(Object o) {RBNode<E> current = header;RBNode<E> parent = header;@SuppressWarnings("unchecked")E e = (E)o;nil.element = e;while(compare(e, current) != 0){parent = current;if(compare(e, current) < 0){current = getLeft(current);}else{current = getRight(current);}}if(current == nil){ //没有找到值为e的数据节点return true;}size--;RBNode<E> node = current;if(current.right != nil){ //替换右子树最小节点parent = current;current = getRight(current);while(current.left != nil){parent = current;current = getLeft(current);}node.element = current.element;//右侧最小节点不是叶子,则继续向下遍历一层,这时可以断定树叶是红色if(current.right != nil){parent = current;current = getRight(current);parent.element = current.element;parent.right = nil;return true;}}else if(current.left != nil){ //替换左子树最大节点parent = current;current = getLeft(node);while(current.right != nil){parent = current;current = getRight(current);}node.element = current.element;//左侧最大节点不是叶子,则继续向下遍历一层,这时可以断定树叶是红色if(current.left != nil){parent = current;current = getLeft(current);parent.element = current.element;parent.left = nil;return true;}}//先调整再删除,因为后面还需要依赖current与parent的位置关系判断if(!current.red){fixRemove(current);}//删除树叶leafif(current == parent.left){parent.left = nil;}else{parent.right = nil;}return true;}private void fixRemove(RBNode<E> current){RBNode<E> parent = current.parent;if(parent == header){return;}if(current == parent.left){RBNode<E> brother = getRight(parent);if(parent.red){ // 1.parent为红色if(getLeft(brother).red){parent.red = false;rotateRight(brother);}}else if(brother.red){ // 2.brother为红色if(!getLeft(brother.left).red && !getRight(brother.left).red){brother.red = false;getLeft(brother).red = true;}else if(getRight(brother.left).red){getRight(brother.left).red = false;rotateRight(brother);}else{getLeft(brother.left).red = false;rotateRight(getLeft(brother));rotateRight(brother);}}else{ // 3. parent和brother都为黑色if(!getLeft(brother).red && !getRight(brother).red){brother.red = true;fixRemove(parent);return;}else if(getRight(brother).red){getRight(brother).red = false;}else{getLeft(brother).red = false;rotateRight(brother);}}//最后一步的调整都是parent左旋rotateLeft(parent);}else{ // 对称情形RBNode<E> brother = getLeft(parent);if(parent.red){if(getRight(brother).red){parent.red = false;rotateLeft(brother);}}else if(brother.red){if(!getLeft(brother.right).red && !getRight(brother.right).red){brother.red = false;getRight(brother).red = true;}else if(getLeft(brother.right).red){getLeft(brother.right).red = false;rotateLeft(brother);}else{getRight(brother.right).red = false;rotateLeft(getRight(brother));rotateLeft(brother);}}else{if(!getLeft(brother).red && !getRight(brother).red){brother.red = true;fixRemove(parent);return;}else if(getLeft(brother).red){getLeft(brother).red = false;}else{getRight(brother).red = false;rotateLeft(brother);}}rotateRight(parent);}}static class RBNode<E> extends Node<E> {RBNode<E> parent;boolean red;RBNode(E e) {super(e);}RBNode(E e, RBNode<E> left, RBNode<E> right) {super(e);this.left = left;this.right = right;}@Overridepublic String toString() {return red ? element.toString() + "(red)" : element.toString();}}}
3.2. MultiTreeMap
public class MultiRBTreeMap<K extends Comparable<? super K>, V> extends MultiBinarySearchTreeMap<K, V> {public MultiRBTreeMap() {}public MultiRBTreeMap(boolean deduplication, boolean asc){super(deduplication, asc);}@Overrideprotected void init() {nil = new RBNode<K, V>(null, null, false, null, null);nil.left = nil;nil.right = nil;header = new RBNode<K, V>(null, null, false, nil, nil);header.next = nil;nil.prev = header;}public V put(K key, V value){nil.key = key;RBNode<K, V> current = (RBNode<K, V>)header;RBNode<K, V> parent = (RBNode<K, V>)header;while(compare(key, current) != 0){parent = current;if(compare(key, current) < 0){current = getLeft(current);}else{current = getRight(current);}if(getLeft(current).isRed && getRight(current).isRed){current = handleReorient(current);}}if(current != nil){if(deduplication){V old = current.value;current.key = key;current.value = value;return old;}else{while((current = getNext(current)).isRepeat);V old = current.prev.value;linkIn(new RBNode<>(key, value, true, nil, nil), current, false);return old;}}current = new RBNode<>(key, value, false, nil, nil);current.parent = parent;if(compare(key, parent) < 0){parent.left = current;linkIn(current, parent, false);}else{parent.right = current;while((parent = getNext(parent)).isRepeat);linkIn(current, parent, false);}//将新数据节点设成红色,如果父亲节点为红色则需要旋转调整handleReorient(current);return null;}private RBNode<K, V> handleReorient(RBNode<K, V> current) {getLeft(current).isRed = false;getRight(current).isRed = false;current.isRed = true;RBNode<K, V> subRoot = current;RBNode<K, V> parent = getParent(current);if(parent.isRed){RBNode<K, V> grand = getParent(parent);grand.isRed = true;//旋转parent, 向外旋转到同一侧if(compare(current.getKey(), grand) != compare(current.getKey(), parent)) {if(compare(current.getKey(), parent) > 0){rotateLeft(parent);}else{rotateRight(parent);}}//旋转grandif(compare(current.getKey(), grand) < 0){subRoot = getLeft(grand);subRoot.isRed = false;rotateRight(grand);}else{subRoot = getRight(grand);subRoot.isRed = false;rotateLeft(grand);}}//直接将根节点置为黑色getRight(header).isRed = false;return subRoot;}private void rotateRight(Node<K, V> node) {Node<K, V> parent = node.parent;Node<K, V> left = node.left;Node<K, V> leftRight = left.right;left.right = node;node.parent = left;node.left = leftRight;leftRight.parent = node;left.parent = parent;if(parent.right == node){parent.right = left;}else{parent.left = left;}}private void rotateLeft(Node<K, V> node) {Node<K, V> parent = node.parent;Node<K, V> right = node.right;Node<K, V> rightLeft = right.left;right.left = node;node.parent = right;node.right = rightLeft;rightLeft.parent = node;right.parent = parent;if(parent.right == node){parent.right = right;}else{parent.left = right;}}private int compare(K key, Entry<K, V> node) {if(node == header){return 1;}else{return key.compareTo(node.getKey());}}private RBNode<K, V> getParent(Node<K, V> node){return (RBNode<K, V>)node.parent;}private RBNode<K, V> getNext(Node<K, V> node){return (RBNode<K, V>)node.next;}private RBNode<K, V> getLeft(Node<K, V> node){return (RBNode<K, V>)node.left;}private RBNode<K, V> getRight(Node<K, V> node){return (RBNode<K, V>)node.right;}@Overrideprotected void remove(Node<K, V> node){RBNode<K, V> rbnode = (RBNode<K, V>)node;RBNode<K, V> current = rbnode;RBNode<K, V> parent = getParent(current);if(current.right != nil){current = getRight(current);while(current.left != nil){parent = current;current = getLeft(current);}locationExchange(rbnode, current);if(rbnode.right != nil){//node.right is redparent = rbnode;rbnode = getRight(rbnode);locationExchange(parent, rbnode);rbnode.right = nil; //交换过位置return;}}else if(current.left != nil){current = getLeft(current);while(current.right != nil){parent = current;current = getRight(current);}locationExchange(rbnode, current);if(rbnode.left != nil){//node.left is redparent = rbnode;rbnode = getLeft(rbnode);locationExchange(parent, rbnode);rbnode.left = nil;return;}}if(!rbnode.isRed){fixRemove(rbnode);}else if(rbnode == parent.left){parent.left = nil;}else{parent.right = nil;}}//这里不能简单的交换节点的值,因为要保证链表和树中删除的是同一个节点实例即nodeprivate void locationExchange(RBNode<K, V> node, RBNode<K, V> current){RBNode<K, V> parent = getParent(node);RBNode<K, V> left = getLeft(node);RBNode<K, V> right = getRight(node);boolean isRed = node.isRed;replaceChild(current.parent, current, node);node.left = current.left;node.left.parent = node;node.right = current.right;node.right.parent = node;node.isRed = current.isRed;replaceChild(parent, node, current);current.left = left;left.parent = current;current.right = right;right.parent = current;current.isRed = isRed;}private void fixRemove(RBNode<K, V> node){RBNode<K, V> parent = getParent(node);if(parent == header){return;}if(node == parent.left){parent.left = nil;RBNode<K, V> brother = getRight(parent);if(parent.isRed){ // 1.parent为红色if(getLeft(brother).isRed){parent.isRed = false;rotateRight(brother);}}else if(brother.isRed){ // 2.brother为红色if(!getLeft(brother.left).isRed && !getRight(brother.left).isRed){brother.isRed = false;getLeft(brother).isRed = true;}else if(getRight(brother.left).isRed){getRight(brother.left).isRed = false;rotateRight(brother);}else{getLeft(brother.left).isRed = false;rotateRight(brother.left);rotateRight(brother);}}else{ // 3. parent和brother都为黑色if(!getLeft(brother).isRed && !getRight(brother).isRed){brother.isRed = true;fixRemove(parent);return;}else if(getRight(brother).isRed){getRight(brother).isRed = false;}else{getLeft(brother).isRed = false;rotateRight(brother);}}//最后一步的调整都是parent左旋rotateLeft(parent);}else{ // 对称情形parent.right = nil;RBNode<K, V> brother = getLeft(parent);if(parent.isRed){if(getRight(brother).isRed){parent.isRed = false;rotateLeft(brother);}}else if(brother.isRed){if(!getLeft(brother.right).isRed && !getRight(brother.right).isRed){brother.isRed = false;getRight(brother).isRed = true;}else if(getLeft(brother.right).isRed){getLeft(brother.right).isRed = false;rotateLeft(brother);}else{getRight(brother.right).isRed = false;rotateLeft(brother.right);rotateLeft(brother);}}else{if(!getLeft(brother).isRed && !getRight(brother).isRed){brother.isRed = true;fixRemove(parent);return;}else if(getLeft(brother).isRed){getLeft(brother).isRed = false;}else{getRight(brother).isRed = false;rotateLeft(brother);}}rotateRight(parent);}}static class RBNode<K, V> extends Node<K, V> {boolean isRed;public RBNode(K key, V value) {super(key, value);}RBNode(K key, V value, boolean isRepeat, Node<K, V> left, Node<K, V> right) {super(key, value, isRepeat, left, right);}@Overridepublic String toString() {String str = super.toString();return isRed ? str + "(red)" : str;}}}
3.3. MultiTreeSet
有了Map之后,就可以很容易的依赖它实现一个性质相似的Set:
public class MultiRBTreeSet<E extends Comparable<? super E>> implements Set<E>{private static final Object V = new Object();private MultiRBTreeMap<E, Object> map;public MultiRBTreeSet(){map = new MultiRBTreeMap<>();}public int size() {return map.size();}public boolean isEmpty() {return map.isEmpty();}public boolean contains(Object o) {return map.containsKey(o);}public Iterator<E> iterator() {return map.keySet().iterator();}public Iterator<E> iterator(E from, boolean fromInclusive, E to, boolean toInclusive) {return map.keySet(from, fromInclusive, to, toInclusive, false).iterator();}public boolean add(E e) {map.put(e, V);return true;}public boolean remove(Object o) {map.removeList(o);return true;}public boolean containsAll(Collection<?> c) {return map.keySet().containsAll(c);}public void clear() {map.clear();}public boolean addAll(Collection<? extends E> collection) {for(E e : collection){map.put(e, V);}return true;}public boolean removeAll(Collection<?> collection) {for(Object o : collection){map.removeList(o);}return true;}public boolean retainAll(Collection<?> c) {// TODO Auto-generated method stubthrow new UnsupportedOperationException();}public Object[] toArray() {// TODO Auto-generated method stubthrow new UnsupportedOperationException();}public <T> T[] toArray(T[] a) {// TODO Auto-generated method stubthrow new UnsupportedOperationException();}}
4. 红黑树的高度
根据规则4,任意节点到其下面各个空节点的黑高相等。那么,记根为x的红黑树的黑高为h(x),那么红黑树中至少包含2^h(x) - 1个黑色节点。根据规则3,在高度为H的红黑树中,任何一条路径至多有一半的红色节点,即至少有一半的黑色节点数,也就是说黑高至少为H/2。如果红黑树的节点数为N,那么有:N >= 2^(H/2) - 1, 即H <= 2lg(N+1)。 既然有了Avl树,为什么还要红黑树?因为红黑树的综合性能要由于Avl树,主要原因树红黑树恢复平衡时需要的旋转次数比Avl树更少,这个网上有很多,但从之前对Avl树的讨论中可以看出,即便在最差的情形中, 最多也只要4次旋转便可恢复平衡。因此,如果说Avl的旋转次数多不应该是指单次删除或者添加中发生的旋转次数,而是很多次插入删除后的一个统计值。另外在Avl树中,即便只需要一次旋转调整,也免不了要依次检查到根节点,这个过程是自下而上的,即从叶节点追溯到根节点,而插入和删除首先都是自上而下的。因此在实现中如果不使用递归的方式将发生两次遍历过程,这也是之前介绍Avl树不实现MultiTreeMap的原因。而在红黑树中,则可以将调整问题缩小在父节点为根的子树中, 这些问题都可以解决,代价只是稍微牺牲了一些平衡性。
https://mp.weixin.qq.com/s/lZQ1MThnkIoRURTnI8ZXYw

浙公网安备 33010602011771号