一、 什么是红黑树?
红黑树,是二叉查找树的一种,相对于AVL树,通过放弃绝对的平衡而提高了插入的速度,它可以不要求节点的左右子树高度差小于2,但保证一颗子树的高度绝对小于另一颗子树高度的2倍。
一棵拥有n个内部结点的红黑树的树高h<=2log(n+1) 。
红黑树性质:
1. 每个结点或红或黑。
2. 根结点为黑色。
3. 每个叶结点(实际上就是NULL指针)都是黑色的。
4. 如果一个结点是红色的,那么它的周边3个节点都是黑色的。(也就是说两个红色节点不可能相连)
5. 对于每个结点,从该结点到其所有子孙叶结点的路径中所包含的黑色结点个数都一样。
二、 左旋和右旋
红黑树中,最基础也是最重要的就是左旋和右旋了,很好理解,就是以某个节点为中心,对它的父节点及子节点进行旋转。
1.左旋:
图:左旋(以C为中心)
说明:以C为中心左旋,C的父节点A将变为C的左子节点,如果C的左子结点有值,则变为C的原父节点A的右子结点。
2.右旋:
图:右旋(以B为中心)
说明:以B为中心右旋,B的父节点A将变为B的右子节点,如果B的右子节点有值,则变为B的原父节点A的右子结点。
三、 添加节点(Insert)(图中所有的NL节点没有画出来)
伪代码:
1 RB-INSERT(T, z) 2 y = NIL 3 x = ROOT[T] 4 while x != NIL 5 y = x 6 if key[x] > key[z] 7 x = left[x] 8 else 9 x = right[x] 10 p[z] = y 11 if y == NIL 12 ROOT[T] = z 13 else 14 if key[y] > key[z] 15 left[y] = z 16 else 17 right[y] = z 18 left[z] = NIL 19 right[z] = NIL 20 color[z] = RED 21 RB-INSERT_FIXUP(T, z) 22 23 RB-INSERT-FIXUP(T, z) 24 while color[p[z]] == RED 25 if p[z] == left[p[p[z]]] 26 y = right[p[p[z]]] 27 //case 1 28 if color[y] == RED 29 color[p[z]] = BLACK 30 color[y] = BLACK 31 color[p[p[z]]] = RED 32 z = p[p[z]] 33 else 34 //case 2 35 if z == right[p[z]] 36 LEFT-ROTATE(T, z) 37 z = left[z] 38 //case 3 39 color[p[z]] = BLACK 40 color[p[p[z]]] = RED 41 RIGHT-ROTATE(T, p[z]) 42 else 43 y = left[p[p[z]]] 44 //case 1 45 if color[y] == RED 46 color[p[z]] = BLACK 47 color[y] = BLACK 48 color[p[p[z]]] = RED 49 z = p[p[z]] 50 else 51 //case 2 52 if z == right[p[z]] 53 RIGHT-ROTATE(T, z) 54 z = right[z] 55 //case 3 56 color[p[z]] = BLACK 57 color[p[p[z]]] = RED 58 LEFT-ROTATE(T, p[z]) 59 60 color[ROOT[T]] = BLACK
总结步骤:
① 插入节点后,当前节点指向插入节点,跳到②
② 如果当前节点为根节点,则将节点颜色变为黑,跳到(结束),否则跳到③
③ 如果当前节点的父节点为黑色,无需进一步处理,跳到(结束),否则跳到④
④ 当前节点的父节点为红色,如果父节点为祖节点的左子节点,则跳到⑤,否则跳到⑧
⑤ case 1:如果叔节点为红色,则父节点和叔节点变黑,祖节点变红,当前节点指向祖节点,跳到②,否则跳到⑥
⑥ case 2:叔节点为黑色,如果当前节点为父节点的右子节点,形成(LR),则沿当前节点进行左旋,当前节点指向当前节点的左子节点,跳到⑦
⑦ case 3:当前节点的父节点变黑,祖节点变红,沿当前节点的父节点进行右旋,跳到②
⑧ 当前节点的父节点为为祖节点的右子节点,则进行⑤-⑦的相反操作
四、 删除节点(Delete)
伪代码:
TREE_SUCCESSOR(z) v = right[z] while left[v] != NIL v = left[v] return v RB-DELETE(T, z) if left[z] == NIL and right[z] == NIL x = z y = z else if left[z] == NIL or right[z] == NIL y = z else y = TREE_SUCCESSOR(z) if left[y] != NIL x = left[y] else if right[y] != NIL x = right[y] else x = y if x != y //delete y from the tree p[x] = p[y] if p[y] == NIL ROOT[T] = x else if y == left[p[y]] left[p[y]] = x else right[p[y]] = x if y != z key[z] = key[y] if color[y] == BLACK RB-DELETE_FIXUP(T, x) if x == y if y == left[p[y]] left[p[y]] = NIL else right[p[y]] = NIL if p[y] == NIL ROOT[T] = NIL return y
RB-DELETE-FIXUP(T, x) while x!= root[T] and color[x] == BLACK if x = left[p[x]] //x is Left w = right[p[x]] //case 1:w is RED if color[w] = RED color[w] = BLACK color[p[x]] = RED LEFT-ROTATE(T, w) w = right[p[x]] //w is BLACK //case 2: right[w] and left[w] is BLACK if color[left[w]] == BLACK and color[right[w]] == BLACK color[w] = RED x = p[x] else //case 3: right[w] is BLACK and left[w] is RED if color[left[w]] == RED color[left[w]] = BLACK color[w] = RED RIGHT-ROTATE(T, left[w]) w = right[p[x]] //case 4:right[w] is RED color[w] = color[p[x]] color[p[x]] = BLACK color[right[w]] = BLACK LEFT-ROTATE(T, w) x = ROOT[T] else //x is Right w = left[p[x]] if color[w] = RED //w is RED color[w] = BLACK color[p[x]] = RED RIGHT-ROTATE(T, w) w = left[p[x]] //w is BLACK if color[right[w]] == BLACK and color[left[w]] == BLACK color[w] = RED x = p[x] else if color[left[w]] == BLACK color[left[w]] = BLACK color[w] = RED LEFT-ROTATE(T, right[w]) w = left[p[x]] color[w] = color[p[x]] color[p[x]] = BLACK color[left[w]] = BLACK RIGHT-ROTATE(T, w) x = ROOT[T] color[x] = BLACK
说明:上面RB-DELETE函数中有部分是红色代码,这部分代码与算法导论中的代码有所区别,主要是针对删除的节点时叶子节点的情况,虽然原有代码也能完成功能,但会增加后面RB-DELETE-FIXUP的操作难度。
总结步骤:
删除节点的操作稍微复杂一些,但还是有一定规则的,主要分成两个步骤:删除节点和修复性质。
删除:
① 如果要删除的节点至少有一个子节点为NIL,则将当前节点指向待删除节点,跳到③,否则跳到②
② 待删除节点有两个子节点,则将当前节点指向待删除节点右子树的最左节点,也就是当前节点在中序遍历时的后继节点,将当前节点的值覆盖待删除节点的值,跳到③
③ 如果当前节点的左子节点不为NIL,则将当前节点的左子节点作为X,否则将当前节点的右子节点作为X,删除当前节点,包括当前节点的父节点、左右子节点、父节点的子节点和子节点的父节点的指针的操作,将当前节点指向X,如果当前节点的颜色为红色,则结束,否则跳到④
平衡修复:
④ 如果当前节点为黑色,且当前节点不为根节点,则跳到⑤,否则结束
⑤ 如果当前节点为父节点的左子节点,则跳到⑥,否则跳到⑩
⑥ case1:当前节点为父节点的左子节点,如果它的兄弟节点W为红色,则使其兄弟节点变黑色,父节点变红色,沿它的兄弟节点W左旋,将W指向当前节点的新兄弟节点,跳到⑦;
⑦ case2:如果W的左子节点和右子节点都为黑色,则将W变红,当前节点指向其父节点,跳到④,否则跳到⑧
⑧ case3:如果W的右子节点为黑色(W的左子节点为红色),则将W的左子节点变黑,W变红,沿W的左子节点右旋,W指向当前节点的新兄弟节点,跳到⑨
⑨ case4:W的颜色变为当前节点的父节点的颜色,当前节点的父节点变为黑色,W的右子节点变为黑色,沿W进行左旋,当前节点指向根节点,跳到④
⑩ 如果当前节点为父节点的右子节点,则完成⑥-⑨的相反操作