紅黑樹是一種自平衡二叉查找樹,具備在最壞狀況下查找、插入、刪除O(log2n)的複雜度。紅黑樹中從根節點到任意一葉子節點的最長路徑不超過最短路徑的兩倍,所以是一種近似平衡的二叉樹。html
紅黑樹的節點具備以下信息:算法
struct RBNode{ int data; //數據 int color; //顏色,紅或者黑 RBNode* parent; //父節點 RBNode* child[2]; int child_dir; //指示當前節點是其父節點的左仍是右子節點 RBNode(int d){ data = d; color = RED; parent = NULL; child[0] = child[1] = NULL; } };
紅黑樹知足以下性質:數據結構
- 每一個節點或者是黑色或者是紅色
- 樹的根節點爲黑色
- 樹的葉節點(NIL)爲黑色(爲了表示方便紅黑樹中全部有效葉子節點<含有數據的葉節點>都添加了額外的葉子節點爲NIL, 這裏是指NIL爲黑色)
- 若是一個節點是紅色,則它的兩個子節點均爲黑色
- 對每一個節點,從該節點到其子孫葉節點上的全部路徑上包含有相同數目的黑節點
對於知足如上條件的紅黑樹,則有如下引理:性能
一棵有n個內節點的紅黑樹的高度至多爲 2*log2(n+1)atom
所以紅黑樹的操做複雜度最壞爲 O(log2n)spa
和AVL樹、Splay樹、Treap同樣,紅黑樹實現其平衡性也是經過節點的旋轉(以及對節點從新染色)來實現。紅黑樹的旋轉具備左旋和右旋兩種,和伸展樹中的旋轉相同,具體見樹的旋轉。.net
紅黑樹的插入和二叉查找樹的插入相同,都是經過二叉樹的性質找到待插入節點應該被插入的位置,而後插入。不過,因爲紅黑樹具備以前提到的五點性質的要求,所以,還須要對紅黑樹進行額外的維護操做。設計
插入以後,可能違反性質二、4,所以須要維護:
(1)若是被插入的節點爲根節點,則違反性質2,所以直接將 根節點的color改成BLACK便可
(2)若是被插入的節點的父節點爲黑色,則不違反任何規則,直接返回
(3)若是被插入節點的父節點爲紅色,且祖父節點(確定)爲黑色,此時細分三種狀況:
* (a) 被插入節點的祖父節點的另外一個子節點(即被插入節點的叔叔節點)爲紅色
* (b) 被插入節點的叔叔節點爲黑色,且被插入節點爲其父節點的右子節點
* (c) 被插入節點的叔叔節點爲黑色,且被插入節點爲其父節點的左子節點code
對於(3.a),則htm
被插入節點的祖父節點的另外一個子節點(即被插入節點的叔叔節點)爲紅色
將當前節點的祖父節點改成紅色,當前節點的父節點和叔叔節點改成黑色,同時當前節點設爲其祖父節點,遞歸執行InsertFix
以前
以後
對於(3.b),則
被插入節點的叔叔節點爲黑色,且被插入節點爲其父節點的右子節點
當前節點和其父節點執行左旋,父節點被旋轉到下方,當前節點更新爲下方的原父節點
以前
以後
對於(3.c),則
被插入節點的叔叔節點爲黑色,且被插入節點爲其父節點的左子節點
父節點變爲黑色,祖父節點變爲紅色,祖父節點和父節點進行右旋
以前
以後
這樣通過以上的旋轉染色操做,能夠保證紅黑樹的性質二、四、5成立。
紅黑樹刪除入和二叉查找樹的刪除相同,都是經過二叉樹的性質找到待刪除節點的位置,若是待刪除節點沒有子節點,則直接刪除,並用空節點頂替該節點;若是待刪除節點只有一個非空子節點,則用該非空子節點頂替待刪除的節點;若是待刪除節點兩個節點均非空,則找到該節點的後繼節點,交換該節點和其後繼節點的數據,再次調用Delete操做。這樣,再次調用後,找到原來節點的後繼節點,此時該後繼節點一定至多隻有一個非空子節點,所以在前面所列舉的狀況下能夠解決。
不過,因爲紅黑樹具備以前提到的五點性質的要求,所以,還須要對紅黑樹進行額外的維護操做,維護操做從頂替被刪除的節點的節點處開始。
若是被刪除的節點爲黑色,則刪除以後,可能會違反紅黑樹的性質5,所以須要維護:設頂替被刪除節點的節點爲當前節點
(1) 若是當前節點爲紅色,則直接將該節點變成黑色,返回便可
(2) 若是當前節點是黑色,且爲根節點,則直接返回便可
(3) 若是當前節點爲黑色,且非根節點,細分爲以下四種狀況:
(a) 當前節點是黑色,且兄弟節點爲紅色(此時其父節點和兄弟節點的子節點一定爲黑色)
(b) 當前節點爲黑色,且兄弟節點爲黑色,且兄弟節點的兩個子節點均爲黑色
(c) 當前節點爲黑色,且兄弟節點是黑色,兄弟節點的左子結點爲紅色,右子節點爲黑色
(d) 當前節點爲黑色,且兄弟節點爲黑色,兄弟節點的右子節點爲紅色,兄弟節點的左子結點顏色任意
對於情形(3.a)
當前節點是黑色,且兄弟節點爲紅色(此時其父節點和兄弟節點的子節點一定爲黑色), 則
把父節點變成紅色,兄弟節點變成黑色,而後對父節點和兄弟節點執行左或右旋操做,以後從新進入算法。
以前
以後
對於情形(3.b)
當前節點是黑色,且兄弟節點爲黑色,且兄弟節點的兩個子節點全爲黑色
把當前節點的兄弟節點變成紅色,而後當前節點變爲其父節點
以前
以後
因爲被刪除節點爲黑色,此時當前節點的那條路徑上的黑色節點的個數就減小1, 不知足性質5,此時將當前節點的兄弟節點染成紅色,當前節點提高爲其父節點。此時,從當前節點向下的路徑上,黑色節點的個數相同,可是和從更高節點到葉節點的路徑相比,黑色節點個數仍然少1.
對於情形(3.c)
當前節點爲黑色,且兄弟節點是黑色,兄弟節點的左子結點爲紅色,右子節點爲黑色
把兄弟節點變成紅色,兄弟節點的左子結點變成黑色,而後對兄弟節點及其左子結點執行右旋操做
以前
以後
對於情形(3.d)
當前節點爲黑色,且兄弟節點爲黑色,兄弟節點的右子節點爲紅色,兄弟節點的左子結點顏色任意 把兄弟節點染成當前節點父節點的顏色,把當前節點父節點染成黑色,兄弟節點右子節點染成黑色,以後對當前節點父節點執行左旋操做
以前
以後
RB-INSERT-FIXUP(T, z) while z.p.color == RED do if z.p == z.p.p.left then y ← z.p.p.right if y.color == RED then z.p.color ← BLACK ▹ Case 1 y.color ← BLACK ▹ Case 1 z.p.p.color ← RED ▹ Case 1 z ← z.p.p ▹ Case 1 else if z == z.p.right then z ← z.p ▹ Case 2 LEFT-ROTATE(T, z) ▹ Case 2 z.p.color ← BLACK ▹ Case 3 z.p.p.color ← RED ▹ Case 3 RIGHT-ROTATE(T, z.p.p) ▹ Case 3 else (same as then clause with "right" and "left" exchanged) T.root.color ← BLACK while x ≠ root[T] and color[x] = BLACK do if x = left[p[x]] then w ← right[p[x]] if color[w] = RED then color[w] ← BLACK ▹ Case 1 color[p[x]] ← RED ▹ Case 1 LEFT-ROTATE(T, p[x]) ▹ Case 1 w ← right[p[x]] ▹ Case 1 if color[left[w]] = BLACK and color[right[w]] = BLACK then color[w] ← RED ▹ Case 2 x ← p[x] ▹ Case 2 else if color[right[w]] = BLACK then color[left[w]] ← BLACK ▹ Case 3 color[w] ← RED ▹ Case 3 RIGHT-ROTATE(T, w) ▹ Case 3 w ← right[p[x]] ▹ Case 3 color[w] ← color[p[x]] ▹ Case 4 color[p[x]] ← BLACK ▹ Case 4 color[right[w]] ← BLACK ▹ Case 4 LEFT-ROTATE(T, p[x]) ▹ Case 4 x ← root[T] ▹ Case 4 else (same as then clause with "right" and "left" exchanged) color[x] ← BLACK
紅黑樹並不追求「徹底平衡」——它只要求部分地達到平衡要求,下降了對旋轉的要求,從而提升了性能。
紅黑樹可以以O(log2n) 的時間複雜度進行搜索、插入、刪除操做。此外,因爲它的設計,任何不平衡都會在三次旋轉以內解決。固然,還有一些更好的,但實現起來更復雜的數據結構 可以作到一步旋轉以內達到平衡,但紅黑樹可以給咱們一個比較「便宜」的解決方案。紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高。