首先,在閱讀文章以前,我但願讀者對二叉樹有必定的瞭解,由於紅黑樹的本質就是一顆二叉樹。因此本篇博客中不在將二叉樹的增刪查的基本操做了,須要瞭解的同窗能夠到我以前寫的一篇關於二叉樹基本操做的博客:http://www.javashuo.com/article/p-vdgloopa-gr.html;html
有隨機數節點組成的二叉樹的平均高度爲logn,因此正常狀況下二叉樹查找的時間複雜度爲O(logn)。可是,根據二叉樹的特性,在最壞的狀況下,好比存儲的是一個有序的數據的話,那麼因此的數據都會造成一條鏈,此時二叉樹的深度爲n,時間複雜度爲O(n)。紅黑樹就是爲了解決這個問題的,它可以保證在任何狀況下樹的深度都保持在logn左右,紅黑樹經過一下約束來完成這個特性:node
一、每一個節點不是紅色就是黑色。學習
二、根節點爲黑色。this
三、每一個葉子節點都是黑色的。spa
四、每一個紅色節點的子節點都是黑色。3d
五、任意節點,到其任意葉節點的全部路徑都包含相同的黑色節點。指針
結構以下圖:code
紅黑樹的基本操做包括刪除和添加。在刪除或者添加一個節點的時候就有可能打破原有的紅黑樹維持的平衡,那麼就須要經過着色和旋轉的方式來使紅黑樹從新達到平衡。着色是很是簡單的,直接將節點的顏色改變就能夠了,多以要理解紅黑樹,就必須須要懂得如何進行旋轉,旋轉又分爲左旋和右轉,兩個操做相反的,因此理解了一個旋轉的操做就很容易理解另外一個旋轉了。視頻
左旋:htm
如圖所示,紅色節點爲旋轉支點,支點往左子樹移動即爲左旋。左旋以後咱們能夠看到原支點的位置被原支點的右子節點代替,新支點的左子節點變爲了原來爲父節點的原支點,新支點的左子節點變爲原支點的右子節點,所以左旋操做總共右3個節點,覺得旋轉前的結構舉例,分別爲紅色節點(原支點),黃色節點(新支點)和L節點。Java代碼實現以下:
/** * 左旋 * @param e 支點 */ private void leftRotate(Entry<K,V> e){ //支點的右子節點 Entry<K,V> right = e.right; //支點右子節點的左子節點 Entry<K,V> rightOfLeft = right.left; //新舊支點的替換 right.parent = e.parent; if (e.parent == null){ root = right; }else { if (e == e.parent.left) e.parent.left = right; else e.parent.right = right; } //將原支點變爲新支點的左節點 right.left = e; e.parent = right; //將新支點的左節點變爲就支點的右節點 e.right = rightOfLeft; if (rightOfLeft != null) rightOfLeft.parent = e; }
由於在紅黑樹中每一個節點都有一個指針指向本身的父節點,父節點也有指針指向子節點,由於在改動一個節點的時候都須要分別改動當前節點和父節點的指向,結合左旋的示意圖,用Java代碼實現起來就不會很困難了。
右旋
右旋操做和左旋相反的,二者互反。依然是紅色做爲旋轉支點,右旋後黃色節點代替了紅色節點原來的位置,黃色節點的右節點旋轉後變爲紅色節點的左節點。Java 代碼實現以下:
/** * 右旋 * @param e 旋轉支點 */ private void rightRotate(Entry<K,V> e){ //原支點的左節點 Entry<K,V> left = e.left; //原支點的左節點的右節點 Entry<K,V> leftOfRight = left.right; //新舊支點的替換 left.parent = e.parent; if (e.parent == null){//支點的父節點爲根節點的狀況 root = left; }else {//非跟節點 if (e == e.parent.left) e.parent.left = left; else e.parent.right = left; } //將原支點變爲新支點的右節點 left.right = e; e.parent = left; //將新支點未旋轉前的右節點變爲轉換後的原支點的左節點 e.left = leftOfRight; if (leftOfRight != null) leftOfRight.parent = e; }
添加節點
首先,在進入主題以前咱們再來回顧一下紅黑樹的5個特色:
一、每一個節點不是紅色就是黑色。
二、根節點爲黑色。
三、每一個葉子節點都是黑色的。
四、每一個紅色節點的子節點都是黑色。
五、任意節點,到其任意葉節點的全部路徑都包含相同的黑色節點。
紅黑樹插入節點與二叉樹是一致的,因此每次添加節點確定是添加到葉子節點上,具體步驟以下:
第一步:將新節點插入到紅黑樹中。
第二步:將新節點設置爲紅色。這裏爲何須要設置成紅色呢?主要是爲了知足特性5,這樣在插入節點後就少解決了一個衝突,也就少一點麻煩。插入完成後,咱們來看一下還有那些特性是有可能發生衝突的,特性1每一個節點不是紅色就是黑色的,這明顯沒有衝突,特性2根節點爲黑色,當插入節點爲根節點的時候就會有衝突了,這種就很簡單了,直接將根節點着色爲黑色便可。特性3每一個葉子節點都是黑色,這個明顯沒有衝突。特性4每一個紅色節點的子節點都是黑色的,這個特性就有可能會衝突,由於在插入新節點的時候咱們沒法肯定新節點的父節點的顏色是黑色的仍是紅色,若是新節點的父節爲黑色,那麼就不會有衝突,不然就會違背了特性4。特性5任意節點,到其任意子節點的全部路徑都包含相同的黑色節點,由於咱們插入的新節點被着色爲紅色,因此並不會影響到每一個路徑的黑色節點的數量,所以也不會有衝突。綜上所訴,那麼在插入新節點的時候,只有特性4有可能發生衝突。
第三步:平衡紅黑樹,使之成爲新的紅黑樹。
根據第二部獲得的結論,咱們能夠知道只有狀況是須要解決衝突的,那就是新節點的父節點爲紅色的時候違背了特性4。接下來咱們將要討論這個問題,由於在新插入一個節點以前是一顆已經平衡了的紅黑樹,所以根據特新4,新節點的祖父節點一定爲黑色。根據這種狀況,咱們又能夠分爲如下四種狀況:
狀況1:新節點爲左節點,叔叔節點爲紅色;
狀況2:新節點爲左節點,叔叔節點爲黑色;
狀況3:新節點爲右節點,叔叔節點爲紅色;
狀況4:新節點爲右節點,叔叔節點爲黑色;
狀況1和狀況3的狀況是同樣的,因此咱們能夠將這兩種狀況看做是一種狀況,這個狀況咱們稍後再討論,而後看一下狀況2和狀況4,經過左旋就能夠轉換成狀況2。
綜上所述,咱們能夠歸結爲3中狀況:
狀況1:叔叔節點是紅色節點;
狀況2:叔叔節點是黑色節點,新節點爲右節點;
狀況3:叔叔節點是黑色節點,新節點爲左節點;
上面我也有提到,當插入新節點時確定是屬於第一種狀況的,而後二、3由1轉換而來,在此以前我但願你以前已經瞭解過遞歸的原理和思想,把局部看做總體的思想,由於這將有助於下面討論的理解。下面咱們將要繼續分析這三種狀況,狀況1這種狀況處理起來比比較簡單,只須要將祖父節點變爲紅色節點,父節點和叔叔節點變爲黑色便可,這僅僅只是當整個紅黑樹只有這幾個節點的時候是能夠了,但事實並不是如此,這僅僅只是達到了局部平衡。
上圖,咱們看到已經達到了局部的平衡,可是,咱們還會有其餘的狀況,那就是祖父節點有可能也會有父節點。那麼又會有兩種狀況,1是祖父節點的父節點多是黑色的,2是多是紅色的,若是黑色那麼整個紅黑樹就達到平衡了。不知道你們根覺到了沒有,這兩種狀況是否是跟新插入一個節點的狀況是一致的,是否是又回到了插入新節點的問題了?因而我將局部收到影響的部分畫出來,如圖:
圖a就是將狀況1重新着色後的部分受影響的節點,固然只是其中的一種狀況,此時咱們將已經平衡的部分去掉就變成的圖b的狀況,這種狀況是否是很熟悉呢?咱們的祖父節點當成新節點,是否是至關於上面討論的狀況1呢?不過與上面討論的狀況不一樣的是,這裏3中可能狀況均可能出現,由於叔叔節點有可能爲紅色或黑色。因此這時候纔有可能出現真正的三種狀況:
狀況1:叔叔節點是紅色節點;
狀況2:叔叔節點是黑色節點,新節點爲右節點;
狀況3:叔叔節點是黑色節點,新節點爲左節點;
若是爲狀況1的話,咱們一層一層的往上平衡就能夠了,當祖父節點爲根節點的時候,咱們直接將根節點着色爲黑色便可,由於祖父節點的兩個子節點都是黑色的,因此變爲黑色後仍然是平衡的。接下來咱們來討論下狀況2和3。
很明顯的,這兩種狀況的右節點多出了一個黑色節點,這種狀況是在狀況1向上着色的時候形成的,即祖父節點由黑色節點變爲了紅色節點。狀況2以父節點爲支點左旋,而後將父節點和新節點互換能夠獲得狀況3:
狀況3進行的操做是,首先將父節點着色爲黑色,祖父節點着色爲紅色,而後以祖父爲支點進行右旋
狀況3旋轉結束後整棵紅黑也已經從新恢復平衡了。單從部分其實並看不出已經平衡了,咱們能夠將三個狀況連起來就能夠看到了,以下圖:
上圖中都是以n節點爲參考點的,其他無關的節點就不標出來了。n節點即爲插入節點,可是除了第一次操做n節點爲真正的新節點,此後的操做所指的n節點只是有助於咱們的理解把他當成新節點。固然,這只是其中的一種狀況,其餘其餘的狀況能夠經過不斷向上旋轉或着色,最終也會達到這種狀況或者頂部是p節點爲根節點的時候,第二種狀況直接將根節點着色爲黑色便可。
總結:
回顧一下紅黑樹的5個特性:
一、節點不是紅色就是黑色。
二、根節點爲黑色。
三、葉子節點爲黑色。
四、每一個紅色節點其子節點必須是黑色節點。
五、任意節點到到其任意的子節點的全部路徑的黑色節點的數量相等。
在插入新節點的時候很顯然,不會違背1和3,若是插入的是根節點直接將根節點着色爲黑色便可,這種狀況能夠忽略不計,因此插入節點時可能會違背了4和5,又由於插入的是紅色節點所以5也不會違背,最後在插入新節點的時候咱們只須要關注特性4就能夠了。當父節點爲紅色的時候跟4有衝突,因此咱們接下來討論的就是這種狀況。咱們知道,在插入新節點以前整顆紅黑樹是平衡的,所以能夠得出一個結論就是祖父節點確定確定是黑色的。咱們如今只關注相關的節點便可,目前,咱們知道了祖父的節點爲黑色,父節點爲紅色,可是叔叔節點的顏色不知道,新節點的位置也不能肯定,因此有2x2中狀況,當叔叔節點爲紅色的時候,兩種狀況的處理方式是一致的,因此最後咱們能夠總結爲3中狀況:
一、叔叔節點爲紅色
二、新節點爲右節點,叔叔節點爲黑色
三、新節點爲左節點,叔叔節點爲黑色
類型 | 描述 | 步驟 | 示意圖 |
狀況1 | 叔叔節點爲紅色 | 一、父節點設爲黑色 二、叔叔節點設爲黑色 三、祖父節點設爲紅色 四、把祖父節點設置爲新節點(當前節點) |
|
狀況2 | 新節點爲右節點,叔叔節點爲黑色 | 一、以父節點爲支點左旋 二、父節點和新節點互換位置 三、把父節點設爲當前節點 |
|
狀況3 | 新節點爲左節點,叔叔節點爲黑色 | 一、父節點設爲黑色 二、祖父節點設爲紅色 三、以祖父節點爲支點右旋 |
|
整合 | 樹a就是表中的狀況1,經過着色後直接轉換成了狀況3,狀況3進行着色旋轉後達到了平衡,當樹b中的叔叔節點爲紅色的時候與樹a一致,循環調用樹a的處理方式,直至達到樹b的狀況或者樹a中的祖父節點到達了根節點,這時候將祖父節點設爲黑色便可。 這種狀況就是由狀況1轉狀況2再轉狀況3,由狀況3從新着色旋轉後達到平衡。 須要注意的是:不是每次插入節點都會出現3中狀況,有可能只出現了2和3,或者只出現了3一種狀況。 |
上面是討論左子樹的問題,由於紅黑色具備堆成性,所以在處理右子樹的時候與處理左子樹相反便可。Java代碼示例以下:
/** * 插入新節點後平衡紅黑樹 * @param e 新節點 */ private void fixAfterInsertion(Entry<K, V> e) { //將新插入節點設置爲紅色 setRed(e); Entry<K,V> p,g,u;//父節點和祖父節點和叔叔節點 Entry<K,V> current = e;//新節點 /** * 這裏經過循環不斷向上平衡 */ while ((p = parentOf(current)) != null && isRed(p)){ g = parentOf(p);//祖父節點 if (p == g.left){ u = g.right; //狀況1:叔叔節點爲紅色 if (u != null && isRed(u)){ setBlack(p);//父節點設爲黑色 setBlack(u);//叔叔節點設爲黑色 setRed(g);//祖父節點設爲紅色 current = g;//把祖父節點設爲當前節點 //繼續向上平衡 continue; } //狀況2:當前節點爲右節點,叔叔節點爲黑色 if (current == p.right){ leftRotate(p);//父節點爲支點左旋 Entry<K,V> tmp = p; p = current;//父節點和當前節點互換 current = tmp;//父節點設爲當前節點 } //狀況3:當前節點爲左節點,叔叔節點爲黑色 setBlack(p);//父節點設爲黑色 setRed(g);//祖父節點設爲紅色 rightRotate(g);//祖父節點爲支點右旋 }else {//相反的操做 u = g.left; if (u != null && isRed(u)){ setBlack(p); setBlack(u); setRed(g); current = g; continue; } if (current == p.left){ rightRotate(p); Entry<K,V> tmp = p; p = current; current = tmp; } setBlack(p); setRed(g); leftRotate(g); } } //最後將根節點設置爲紅色 setBlack(root); }
刪除節點
在二叉樹分析一文中已經說過,刪除一個節點的時候有3中狀況:
一、刪除節點沒有子節點
二、刪除節點只有一個子節點
三、刪除節點有兩個子節點
首先,咱們逐個來分析每種狀況刪除節點後對整顆紅黑樹的平衡性的影響。在刪除節點時候紅黑樹的特性1,2,3確定不會違背,因此只須要考慮特性4,5便可。
對於狀況1,確定不會違背特性4,若是刪除節點爲紅色,那麼對整顆紅黑樹的平衡性都不會影響,若是是黑色則違背了特性5,咱們先將這種狀況記錄下來,稍後再進一步討論。
對於狀況2,有可能刪除的是左子樹或右子樹,暫且不討論。若是刪除的節點爲紅色,不影響平衡性,若是刪除的是黑色,那麼確定會和特性5有衝突,當刪除節點的父節點爲紅色,子節點爲紅色是也和特性4有衝突。
對於狀況3,其實最後刪除的是它的替代節點,根據替代節點的特色,最終實際上是回到了1這種狀況或者狀況2。
總結上面的3種狀況可獲得一個結論,只有刪除節點爲黑色時纔會破壞紅黑樹原來的平衡,因在刪除節點以前紅黑樹是出於平衡狀態的,刪除以後很明顯的其兄弟節點分支必然比刪除節點的分支多了一個黑色的節點,所以咱們只須要改變兄弟節點的顏色便可,咱們只討論左節點,右節點對稱。
1、刪除節點的兄弟節點是紅色
將兄弟節點設爲黑色,父節點設爲紅色,以父節點爲支點左旋轉,而後將父節點的右節點放到兄弟節點上:
2、兄弟節點是黑色的,兄弟的兩個子節點也都是黑色的
兄弟節點設爲紅色,把父節點設置爲新的刪除節點:
3、兄弟節點是黑色的,且兄弟節點的左子節點是紅色,右子節點是黑色
將兄弟節點的左子節點設爲黑色,兄弟節點設爲紅色,以兄弟節點爲支點右旋,把父節點的右節點設置爲兄弟節點
4、兄弟節點是黑色的,且兄弟節點的右子節點是紅色,左子節點任意顏色
把兄弟節點的設爲父節點的顏色,父節點設爲黑色,父節點的右節點設爲黑色,父節點爲支點左旋
刪除的Java代碼示例:
public V remove(Object key){ if (key == null) return null; Entry<K,V> delEntry; delEntry = getEntry(key); if (delEntry == null) return null; size--; Entry<K,V> p = delEntry.parent; if (delEntry.right == null && delEntry.left == null){ if (p == null){ root = null; }else { if (p.left == delEntry){ p.left = null; }else { p.right = null; } } }else if (delEntry.right == null){//只有左節點 Entry<K,V> lc = delEntry.left; if (p == null) { lc.parent = null; root = lc; } else { if (delEntry == p.left){ p.left = lc; }else { p.right = lc; } lc.parent = p; } }else if (delEntry.left == null){//只有右節點 Entry<K,V> rc = delEntry.right; if (p == null) { rc.parent = null; root = rc; }else { if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } }else {//有兩個節點,找到後繼節點,將值賦給刪除節點,而後將後繼節點刪除掉便可 Entry<K,V> successor = successor(delEntry);//獲取到後繼節點 boolean color = successor.color; V old = delEntry.value; delEntry.value = successor.value; delEntry.key = successor.key; if (delEntry.right == successor){//後繼節點爲右子節點, if (successor.right != null) {//右子節點有右子節點 delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子節點沒有子節點 delEntry.right = null; } }else { successor.parent.left = null; } if (color == BLACK) //fixUpAfterRemove(child,parent); return old; } V old = delEntry.value; if (delEntry.color == BLACK)//刪除爲黑色時,須要從新平衡樹 if (delEntry.right != null)//刪除節點的子節點只有右節點 fixUpAfterRemove(delEntry.right,delEntry.parent); else if (delEntry.left != null)//刪除節點只有左節點 fixUpAfterRemove(delEntry.left,delEntry.parent); else fixUpAfterRemove(null,delEntry.parent); delEntry.parent = null; delEntry.left = null; delEntry.right = null; return old; } private Entry<K, V> getEntry(Object key) { if (key == null) return null; Entry<K, V> delEntry = null; Entry<K, V> current = root; int ret; if (comparator == null){ Comparable<K> k = (Comparable<K>) key; while (current != null){ ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } }else { for (;current != null;){ ret = comparator.compare(current.key, (K) key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } } return delEntry; } //node表示待修正的節點,即後繼節點的子節點(由於後繼節點被挪到刪除節點的位置去了) private void fixUpAfterRemove(Entry<K, V> node,Entry<K,V> parent) { Entry<K,V> other; while((node == null || isBlack(node)) && (node != root)) { if(parent.left == node) { //node是左子節點,下面else與這裏的恰好相反 other = parent.right; //node的兄弟節點 if(isRed(other)) { //case1: node的兄弟節點other是紅色的 setBlack(other); setRed(parent); leftRotate(parent); other = parent.right; } //case2: node的兄弟節點other是黑色的,且other的兩個子節點也都是黑色的 if((other.left == null || isBlack(other.left)) && (other.right == null || isBlack(other.right))) { setRed(other); node = parent; parent = parentOf(node); } else { //case3: node的兄弟節點other是黑色的,且other的左子節點是紅色,右子節點是黑色 if(other.right == null || isBlack(other.right)) { setBlack(other.left); setRed(other); rightRotate(other); other = parent.right; } //case4: node的兄弟節點other是黑色的,且other的右子節點是紅色,左子節點任意顏色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.right); leftRotate(parent); node = this.root; break; } } else { //與上面的對稱 other = parent.left; if (isRed(other)) { // Case 1: node的兄弟other是紅色的 setBlack(other); setRed(parent); rightRotate(parent); other = parent.left; } if ((other.left==null || isBlack(other.left)) && (other.right==null || isBlack(other.right))) { // Case 2: node的兄弟other是黑色,且other的倆個子節點都是黑色的 setRed(other); node = parent; parent = parentOf(node); } else { if (other.left==null || isBlack(other.left)) { // Case 3: node的兄弟other是黑色的,而且other的左子節點是紅色,右子節點爲黑色。 setBlack(other.right); setRed(other); leftRotate(other); other = parent.left; } // Case 4: node的兄弟other是黑色的;而且other的左子節點是紅色的,右子節點任意顏色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.left); rightRotate(parent); node = this.root; break; } } } if (node!=null) setBlack(node); } private Entry<K, V> successor(Entry<K, V> delEntry) { Entry<K,V> r = delEntry.right;//assert r != null; while (r.left != null){ r = r.left; } return r; }
完整的代碼示例:
public class MyTreeMap<K,V> { private static final boolean BLACK = true; private static final boolean RED = false; private Entry<K,V> root; private int size = 0; private final Comparator<K> comparator; MyTreeMap(){ comparator =null; } public MyTreeMap(Comparator comparator){ this.comparator = comparator; } public V put(K key,V value){ if (root == null){ root = new Entry<>(key,value,null); size++; return null; }else { int ret = 0; Entry<K,V> p = null; Entry<K,V> current = root; if (comparator == null){ if (key == null) throw new NullPointerException("key = null"); Comparable<K> k = (Comparable<K>) key; while (current != null){ p =current; ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if(ret > 0) current = current.right; else { current.value = value; return current.value; } } }else { do { p = current; ret = comparator.compare(key,current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else { current.value = value; return value; } }while (current != null); } Entry<K,V> e = new Entry<>(key,value,p); if (ret < 0) p.left = e; else p.right = e; size++; fixAfterInsertion(e); return e.value; } } /** * 插入新節點後平衡紅黑樹 * @param e 新節點 */ private void fixAfterInsertion(Entry<K, V> e) { //將新插入節點設置爲紅色 setRed(e); Entry<K,V> p,g,u;//父節點和祖父節點和叔叔節點 Entry<K,V> current = e;//新節點 /** * 這裏經過循環不斷向上平衡 */ while ((p = parentOf(current)) != null && isRed(p)){ g = parentOf(p);//祖父節點 if (p == g.left){ u = g.right; //狀況1:叔叔節點爲紅色 if (u != null && isRed(u)){ setBlack(p);//父節點設爲黑色 setBlack(u);//叔叔節點設爲黑色 setRed(g);//祖父節點設爲紅色 current = g;//把祖父節點設爲當前節點 //繼續向上平衡 continue; } //狀況2:當前節點爲右節點,叔叔節點爲黑色 if (current == p.right){ leftRotate(p);//父節點爲支點左旋 Entry<K,V> tmp = p; p = current;//父節點和當前節點互換 current = tmp;//父節點設爲當前節點 } //狀況3:當前節點爲左節點,叔叔節點爲黑色 setBlack(p);//父節點設爲黑色 setRed(g);//祖父節點設爲紅色 rightRotate(g);//祖父節點爲支點右旋 }else {//相反的操做 u = g.left; if (u != null && isRed(u)){ setBlack(p); setBlack(u); setRed(g); current = g; continue; } if (current == p.left){ rightRotate(p); Entry<K,V> tmp = p; p = current; current = tmp; } setBlack(p); setRed(g); leftRotate(g); } } //最後將根節點設置爲紅色 setBlack(root); } public boolean containsKey(Object key){ return getEntry(key) != null; } public Set<Entry<K,V>> entrySet(){ Set<Entry<K,V>> list = new HashSet<>(size + 4); entries(root,list); return list; } private void entries(Entry<K,V> e,Set<Entry<K,V>> list){ if (e != null){ entries(e.left,list); list.add(e); entries(e.right,list); } } public boolean containsValue(V v){ return values().contains(v); } public V get(Object key){ Entry<K, V> entry = getEntry(key); return entry == null ? null : entry.getValue(); } private void setColor(Entry<K,V> e,boolean color){ if (e != null) e.color = color; } private void setRed(Entry<K,V> e){ setColor(e,RED); } private void setBlack(Entry<K,V> e){ setColor(e,BLACK); } private void setParent(Entry<K,V> e,Entry<K,V> p){ if (e != null) e.parent = p; } private boolean isBlack(Entry<K,V> e){ return colorOf(e) == BLACK; } private boolean isRed(Entry<K,V> e){ return !isBlack(e); } private Entry<K,V> parentOf(Entry<K,V> e){ return e == null ? null : e.parent; } private boolean colorOf(Entry<K,V> e){ return e == null ? BLACK : e.color; } /** * 右旋 * @param e 旋轉支點 */ private void rightRotate(Entry<K,V> e){ //原支點的左節點 Entry<K,V> left = e.left; //原支點的左節點的右節點 Entry<K,V> leftOfRight = left.right; //新舊支點的替換 left.parent = e.parent; if (e.parent == null){//支點的父節點爲根節點的狀況 root = left; }else {//非跟節點 if (e == e.parent.left) e.parent.left = left; else e.parent.right = left; } //將原支點變爲新支點的右節點 left.right = e; e.parent = left; //將新支點未旋轉前的右節點變爲轉換後的原支點的左節點 e.left = leftOfRight; if (leftOfRight != null) leftOfRight.parent = e; } /** * 左旋 * @param e 支點 */ private void leftRotate(Entry<K,V> e){ //支點的右子節點 Entry<K,V> right = e.right; //支點右子節點的左子節點 Entry<K,V> rightOfLeft = right.left; //新舊支點的替換 right.parent = e.parent; if (e.parent == null){ root = right; }else { if (e == e.parent.left) e.parent.left = right; else e.parent.right = right; } //將原支點變爲新支點的左節點 right.left = e; e.parent = right; //將新支點的左節點變爲就支點的右節點 e.right = rightOfLeft; if (rightOfLeft != null) rightOfLeft.parent = e; } public int getDeep(){ return deep(root); } private int deep(Entry<K,V> e){ int deep = 0; if (e != null){ int leftDeep = deep(e.left); int rightDeep = deep(e.right); deep = leftDeep > rightDeep ? leftDeep + 1 : rightDeep + 1; } return deep; } public V remove(Object key){ if (key == null) return null; Entry<K,V> delEntry; delEntry = getEntry(key); if (delEntry == null) return null; size--; Entry<K,V> p = delEntry.parent; if (delEntry.right == null && delEntry.left == null){ if (p == null){ root = null; }else { if (p.left == delEntry){ p.left = null; }else { p.right = null; } } }else if (delEntry.right == null){//只有左節點 Entry<K,V> lc = delEntry.left; if (p == null) { lc.parent = null; root = lc; } else { if (delEntry == p.left){ p.left = lc; }else { p.right = lc; } lc.parent = p; } }else if (delEntry.left == null){//只有右節點 Entry<K,V> rc = delEntry.right; if (p == null) { rc.parent = null; root = rc; }else { if (delEntry == p.left) p.left = rc; else p.right = rc; rc.parent = p; } }else {//有兩個節點,找到後繼節點,將值賦給刪除節點,而後將後繼節點刪除掉便可 Entry<K,V> successor = successor(delEntry);//獲取到後繼節點 boolean color = successor.color; V old = delEntry.value; delEntry.value = successor.value; delEntry.key = successor.key; if (delEntry.right == successor){//後繼節點爲右子節點, if (successor.right != null) {//右子節點有右子節點 delEntry.right = successor.right; successor.right.parent = delEntry; }else {//右子節點沒有子節點 delEntry.right = null; } }else { successor.parent.left = null; } if (color == BLACK) //fixUpAfterRemove(child,parent); return old; } V old = delEntry.value; if (delEntry.color == BLACK)//刪除爲黑色時,須要從新平衡樹 if (delEntry.right != null)//刪除節點的子節點只有右節點 fixUpAfterRemove(delEntry.right,delEntry.parent); else if (delEntry.left != null)//刪除節點只有左節點 fixUpAfterRemove(delEntry.left,delEntry.parent); else fixUpAfterRemove(null,delEntry.parent); delEntry.parent = null; delEntry.left = null; delEntry.right = null; return old; } private Entry<K, V> getEntry(Object key) { if (key == null) return null; Entry<K, V> delEntry = null; Entry<K, V> current = root; int ret; if (comparator == null){ Comparable<K> k = (Comparable<K>) key; while (current != null){ ret = k.compareTo(current.key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } }else { for (;current != null;){ ret = comparator.compare(current.key, (K) key); if (ret < 0) current = current.left; else if (ret > 0) current = current.right; else{ delEntry = current; break; } } } return delEntry; } //node表示待修正的節點,即後繼節點的子節點(由於後繼節點被挪到刪除節點的位置去了) private void fixUpAfterRemove(Entry<K, V> node,Entry<K,V> parent) { Entry<K,V> other; while((node == null || isBlack(node)) && (node != root)) { if(parent.left == node) { //node是左子節點,下面else與這裏的恰好相反 other = parent.right; //node的兄弟節點 if(isRed(other)) { //case1: node的兄弟節點other是紅色的 setBlack(other); setRed(parent); leftRotate(parent); other = parent.right; } //case2: node的兄弟節點other是黑色的,且other的兩個子節點也都是黑色的 if((other.left == null || isBlack(other.left)) && (other.right == null || isBlack(other.right))) { setRed(other); node = parent; parent = parentOf(node); } else { //case3: node的兄弟節點other是黑色的,且other的左子節點是紅色,右子節點是黑色 if(other.right == null || isBlack(other.right)) { setBlack(other.left); setRed(other); rightRotate(other); other = parent.right; } //case4: node的兄弟節點other是黑色的,且other的右子節點是紅色,左子節點任意顏色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.right); leftRotate(parent); node = this.root; break; } } else { //與上面的對稱 other = parent.left; if (isRed(other)) { // Case 1: node的兄弟other是紅色的 setBlack(other); setRed(parent); rightRotate(parent); other = parent.left; } if ((other.left==null || isBlack(other.left)) && (other.right==null || isBlack(other.right))) { // Case 2: node的兄弟other是黑色,且other的倆個子節點都是黑色的 setRed(other); node = parent; parent = parentOf(node); } else { if (other.left==null || isBlack(other.left)) { // Case 3: node的兄弟other是黑色的,而且other的左子節點是紅色,右子節點爲黑色。 setBlack(other.right); setRed(other); leftRotate(other); other = parent.left; } // Case 4: node的兄弟other是黑色的;而且other的左子節點是紅色的,右子節點任意顏色 setColor(other, colorOf(parent)); setBlack(parent); setBlack(other.left); rightRotate(parent); node = this.root; break; } } } if (node!=null) setBlack(node); } private Entry<K, V> successor(Entry<K, V> delEntry) { Entry<K,V> r = delEntry.right;//assert r != null; while (r.left != null){ r = r.left; } return r; } List<V> values(){ List<V> set = new ArrayList<>(size+4); midIterator(root,set); return set; } private void midIterator(Entry<K,V> e, List<V> values){ if (e != null){ midIterator(e.left,values); values.add(e.value); midIterator(e.right,values); } } public void clear(){ clear(root); root = null; } private void clear(Entry<K,V> node) { if (node != null){ clear(node.left); node.left = null; clear(node.right); node.right = null; } } public int size(){return size;} static final class Entry<K,V>{ private K key; private V value; private Entry<K,V> left; private Entry<K,V> right; private Entry<K,V> parent; private boolean color = BLACK; Entry(K key,V value,Entry<K,V> parent){ this.key = key; this.value = value; this.parent = parent; } public K getKey() { return key; } public V getValue() { return value; } } }
到此,紅黑樹的添加刪除操做已經所有講完了,若是文中有什麼錯誤或不懂得地方,隨時歡迎你們指出討論。你們也能夠關注公衆號:【Java解憂雜貨鋪】,裏面會不定時得發佈一些技術幹活,和學習視頻。