Redis Shell詳解
AVL (平衡二叉樹:追求"完全平衡")樹的另一種變種是紅黑樹(Red-Black-Tree:只要求部分達到平衡)其就是一個二叉查詢樹。對紅黑樹的操作在最壞情形下花費 時間。紅黑樹是具有下列著色性質的二叉查詢樹:
【1】每個節點或者是黑色,或者是紅色。
【2】根是黑色的;
【3】每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!];
【4】如果一個節點是紅色的,則它的子節點必須是黑色的;
【5】從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點;
一、基本概念
左旋(盜取網上一張動圖如下)以父節點 E 為旋轉點,將 S 左旋為 E 的父節點,同時將 S 的左子樹修改為 E 的右子樹。右旋是怎樣的呢?不就是左旋的結果,又給還原回去的樣子麼。哈哈
二、紅黑樹的新增操作流程
【第一步】:將紅黑樹當作一顆二叉查詢樹,將節點插入。紅黑樹本身就是一顆二叉查詢樹,將節點插入後,該樹仍然是一顆二叉查詢樹。也就意味著,樹的鍵值仍然是有序的。此外,無論是左旋還是右旋,若旋轉之前這棵樹是二叉查詢樹,旋轉之後它一定還是二叉查詢樹。這也就意味著,任何的旋轉和重新著色操作,都不會改變它仍然是一顆二叉查詢樹的事實。
【第二步
【第三步】:通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。第二步中,將插入節點著色為"紅色"之後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
【1】對於"特性(1)",顯然不會違背了。因為我們已經將它塗成紅色了。
【2】對於"特性(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當作二叉查詢樹,然後執行的插入操作。而根據二叉查詢數的特點,插入操作不會改變根節點。所以,根節點仍然是黑色。
【3】對於"特性(3)",顯然不會違背了。這裡的葉子節點是指的空葉子節點
【4】對於"特性(4)",是有可能違背的!那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構造成紅黑樹了。
三、紅黑樹插入案例演示
【1】改變顏色:最簡單,紅變黑、黑邊紅(場景描述:當前節點<11節點>為紅色,父節點<12節點>也為紅色,叔叔節點<14節點>也是紅色此種情況就採用變顏色的方式。將父節點、叔叔節點設定為黑色;爺爺節點變為紅色);舉個栗子:如下我們插入了一個節點<11節點>(圖一),符合我們上面描述的場景,對其進行變色。變色後為圖二,但存在兩個連續的紅色節點,此時不符合變色規則,就需要採用左旋轉。
四、紅黑樹程式碼演示
紅黑樹的應用比較廣泛,主要是用它來儲存有序的資料,它的時間複雜度是O(lgn),效率非常之高。例如,Java集合中的 TreeSet和 TreeMap;
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】被刪除節點有兩個兒子。那麼,先找出它的後繼節點;然後把“它的後繼節點的內容”複製給“該節點的內容”;之後,刪除“它的後繼節點”。
【第二步】:通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。因為"第一步"中刪除節點之後,可能會違背紅黑樹的特性。所以需要通過"旋轉和重新著色"來修正該樹,使之重新成為一棵紅黑樹。