1. 程式人生 > 實用技巧 >Redis Shell詳解

Redis Shell詳解

AVL平衡二叉樹:追求"完全平衡")樹的另一種變種是紅黑樹Red-Black-Tree:只要求部分達到平衡)其就是一個二叉查詢樹。對紅黑樹的操作在最壞情形下花費 時間。紅黑樹是具有下列著色性質的二叉查詢樹:
【1】每個節點或者是黑色,或者是紅色。
【2】根是黑色的;
【3】每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!];
【4】如果一個節點是紅色的,則它的子節點必須是黑色的;
【5】從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點

有了上面的幾個性質作為限制,即可避免二叉查詢樹退化成單鏈表的情況。但是,僅僅避免這種情況還不夠,這裡還要考慮某個節點到其每個葉子節點路徑長度的問題。如果某些路徑長度過長,那麼,在對這些路徑上的結點進行增刪查操作時,效率也會大大降低。這個時候性質3和性質4用途就凸顯了,有了這兩個性質作為約束,即可保證任意節點到其每個葉子節點路徑最長不會超過最短路徑的2倍

。紅黑樹法則的一個結論是,紅黑樹的高度最多是,因此查詢操作保證是一種對樹的操作。和往常一樣,困難在於將一個新節點插入到樹中。如何繼續維持樹的平衡,通常使用改變樹的顏色樹的旋轉。預設插入的節點都為紅色,不然就會失衡。下面我們就看個例子

一、基本概念


左旋(盜取網上一張動圖如下)以父節點 E 為旋轉點,將 S 左旋為 E 的父節點,同時將 S 的左子樹修改為 E 的右子樹。右旋是怎樣的呢?不就是左旋的結果,又給還原回去的樣子麼。哈哈

二、紅黑樹的新增操作流程


第一步將紅黑樹當作一顆二叉查詢樹,將節點插入。紅黑樹本身就是一顆二叉查詢樹,將節點插入後,該樹仍然是一顆二叉查詢樹。也就意味著,樹的鍵值仍然是有序的。此外,無論是左旋還是右旋,若旋轉之前這棵樹是二叉查詢樹,旋轉之後它一定還是二叉查詢樹。這也就意味著,任何的旋轉和重新著色操作,都不會改變它仍然是一顆二叉查詢樹的事實。
第二步

將插入的節點著色為"紅色"。為什麼著色成紅色,而不是黑色呢?為什麼呢?在回答之前,我們需要重新溫習一下紅黑樹的特性:將插入的節點著色為紅色,不會違背"特性(5)"!少違背一條特性,就意味著我們需要處理的情況越少。接下來,就要努力的讓這棵樹滿足其它性質即可;滿足了的話,它就又是一顆紅黑樹了。
第三步通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。第二步中,將插入節點著色為"紅色"之後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
【1】對於"特性(1)",顯然不會違背了。因為我們已經將它塗成紅色了。
【2】對於"特性(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當作二叉查詢樹,然後執行的插入操作。而根據二叉查詢數的特點,插入操作不會改變根節點。所以,根節點仍然是黑色。
【3】對於"特性(3)",顯然不會違背了。這裡的葉子節點是指的空葉子節點
,插入非空節點並不會對它們造成影響。
【4】對於"特性(4)",是有可能違背的!那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構造成紅黑樹了。

三、紅黑樹插入案例演示


【1】改變顏色:最簡單,紅變黑、黑邊紅(場景描述:當前節點<11節點>為紅色,父節點<12節點>也為紅色,叔叔節點<14節點>也是紅色此種情況就採用變顏色的方式。將父節點、叔叔節點設定為黑色;爺爺節點變為紅色);舉個栗子:如下我們插入了一個節點<11節點>(圖一),符合我們上面描述的場景,對其進行變色。變色後為圖二,但存在兩個連續的紅色節點,此時不符合變色規則,就需要採用左旋轉。

【2】左旋:當前節點<10節點>為紅色,父節點<13節點>為紅色,叔叔節點<14節點>為黑色,且當前節點是右子樹時。左旋以父節點作為左旋。

【3】右旋:當前節點為紅色,父節點<13節點>為紅色,叔叔節點<18節點>為黑色,且當前節點是左子樹時。右旋(① 將父節點<13節點>變為黑色;② 爺爺節點<15節點>變為紅色;③ 爺爺節點<15節點>旋轉)

四、紅黑樹程式碼演示


紅黑樹的應用比較廣泛,主要是用它來儲存有序的資料,它的時間複雜度是O(lgn),效率非常之高。例如,Java集合中的 TreeSetTreeMap

 1 /**
 2  * 紅黑樹的插入和左旋方法
 3  */
 4 public class RedBlackTree {
 5 
 6     //定義兩種顏色
 7     private final String R = "red";    //
 8     private final String B = "black";  // 9     //定義一個 Root 節點
10     private Node root = null;
11 
12     //建立一個 Node 節點物件
13     class Node{
16         private int data; //資料
17         private String color = R; //顏色
18         private Node right;  //右子樹
19         private Node left;   //左子樹
20         private Node parent; //父節點
21 
22         //構造器,建立節點的時候需要出入資料即可(父節點)
23         public Node(int data){
24             this.data = data;
25         }
27     }
28 
29     //插入方法
30     public void insert(Node node, int data){
31         //插入的大,說明插入到右子樹
32         if(node.data < data){
33             //如果為空則直接插入
34             if(node.right == null){
35                 node.right = new Node(data);
36             }else{
37                 //遞迴呼叫 insert ****************
38                 insert(node.right,data);
39             }
40         //否則插入到左子樹
41         }else{
42             node.left = new Node(data);
43             //如果為空則直接插入
44             if(node.left == null){
45                 node.left = new Node(data);
46             }else{
47                 //遞迴呼叫 insert
48                 insert(node.left,data);
49             }
50         }
51     }
52 
53     //左旋規則:按照動圖的規則來寫
54     public void leftRotate(Node node){
55         //根節點
56         if(node.parent == null){
57             Node E = root;
58             Node S = E.right;
59 
60             //第一步 將E 的父節點賦值為S
61             E.parent = S;
62             //第二步 將E 的右節點賦值為S的左節點
63             E.right = S.left;
64             //第三步 將S 的左節點賦值為 E
65             S.left = E;
66             //S 的parent 賦值為 null
67             S.parent = null;
68         }
69     }
70 }

五、紅黑樹的結點刪除操作


將紅黑樹內的某一個節點刪除。需要執行的操作依次是:首先,將紅黑樹當作一顆二叉查詢樹,將該節點從二叉查詢樹中刪除;然後,通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。詳細描述如下:
第一步將紅黑樹當作一顆二叉查詢樹,將節點刪除。這和"刪除常規二叉查詢樹中刪除節點的方法是一樣的"。分3種情況:
【1】被刪除節點沒有兒子,即為葉節點。那麼,直接將該節點刪除就OK了。
【2】被刪除節點只有一個兒子。那麼,直接刪除該節點,並用該節點的唯一子節點頂替它的位置。
【3】被刪除節點有兩個兒子。那麼,先找出它的後繼節點;然後把“它的後繼節點的內容”複製給“該節點的內容”;之後,刪除“它的後繼節點”。
第二步通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。因為"第一步"中刪除節點之後,可能會違背紅黑樹的特性。所以需要通過"旋轉和重新著色"來修正該樹,使之重新成為一棵紅黑樹。