http://cmsblogs.com/?p=1013 原文出處。html
TreeMap的實現是紅黑樹算法的實現,因此要了解TreeMap就必須對紅黑樹有必定的瞭解,其實這篇博文的名字叫作:根據紅黑樹的算法來分析TreeMap的實現,可是爲了與Java提升篇系列博文保持一致仍是叫作TreeMap比較好。經過這篇博文你能夠得到以下知識點:java
一、紅黑樹的基本概念。算法
二、紅黑樹增長節點、刪除節點的實現過程。api
三、紅黑樹左旋轉、右旋轉的複雜過程。數據結構
四、Java 中TreeMap是如何經過put、deleteEntry兩個來實現紅黑樹增長、刪除節點的。oracle
我想經過這篇博文你對TreeMap必定有了更深的認識。好了,下面先簡單普及紅黑樹知識。.net
紅黑樹又稱紅-黑二叉樹,它首先是一顆二叉樹,它具體二叉樹全部的特性。同時紅黑樹更是一顆自平衡的排序二叉樹。code
咱們知道一顆基本的二叉樹他們都須要知足一個基本性質--即樹中的任何節點的值大於它的左子節點,且小於它的右子節點。按照這個基本性質使得樹的檢索效率大大提升。咱們知道在生成二叉樹的過程是很是容易失衡的,最壞的狀況就是一邊倒(只有右/左子樹),這樣勢必會致使二叉樹的檢索效率大大下降(O(n)),因此爲了維持二叉樹的平衡,大牛們提出了各類實現的算法,如:AVL,SBT,伸展樹,TREAP ,紅黑樹等等。htm
平衡二叉樹必須具有以下特性:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹。也就是說該二叉樹的任何一個等等子節點,其左右子樹的高度都相近。blog
紅黑樹顧名思義就是節點是紅色或者黑色的平衡二叉樹,它經過顏色的約束來維持着二叉樹的平衡。對於一棵有效的紅黑樹二叉樹而言咱們必須增長以下規則:
一、每一個節點都只能是紅色或者黑色
二、根節點是黑色
三、每一個葉節點(NIL節點,空節點)是黑色的。
四、若是一個結點是紅的,則它兩個子節點都是黑的。也就是說在一條路徑上不能出現相鄰的兩個紅色結點。
五、從任一節點到其每一個葉子的全部路徑都包含相同數目的黑色節點。
這些約束強制了紅黑樹的關鍵性質: 從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長。結果是這棵樹大體上是平衡的。由於操做好比插入、刪除和查找某個值的最壞狀況時間都要求與樹的高度成比例,這個在高度上的理論上限容許紅黑樹在最壞狀況下都是高效的,而不一樣於普通的二叉查找樹。因此紅黑樹它是複雜而高效的,其檢索效率O(log n)。下圖爲一顆典型的紅黑二叉樹。
對於紅黑二叉樹而言它主要包括三大基本操做:左旋、右旋、着色。
左旋 右旋
>>>>>>迴歸主角:TreeMap<<<<<<
TreeMap的定義以下:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable
TreeMap繼承AbstractMap,實現NavigableMap、Cloneable、Serializable三個接口。其中AbstractMap代表TreeMap爲一個Map即支持key-value的集合, NavigableMap(更多)則意味着它支持一系列的導航方法,具有針對給定搜索目標返回最接近匹配項的導航方法 。
TreeMap中同時也包含了以下幾個重要的屬性:
//比較器,由於TreeMap是有序的,經過comparator接口咱們能夠對TreeMap的內部排序進行精密的控制 private final Comparator<? super K> comparator; //TreeMap紅-黑節點,爲TreeMap的內部類 private transient Entry<K,V> root = null; //容器大小 private transient int size = 0; //TreeMap修改次數 private transient int modCount = 0; //紅黑樹的節點顏色--紅色 private static final boolean RED = false; //紅黑樹的節點顏色--黑色 private static final boolean BLACK = true;
對於葉子節點Entry是TreeMap的內部類,它有幾個重要的屬性:
//鍵 K key; //值 V value; //左孩子 Entry<K,V> left = null; //右孩子 Entry<K,V> right = null; //父親 Entry<K,V> parent; //顏色 boolean color = BLACK;
注:前面只是開胃菜,下面是本篇博文的重中之重,在下面兩節我將重點講解treeMap的put()、delete()方法。經過這兩個方法咱們會了解紅黑樹增長、刪除節點的核心算法。
在瞭解TreeMap的put()方法以前,咱們先了解紅黑樹增長節點的算法。
紅黑樹在新增節點過程當中比較複雜,複雜歸複雜它一樣必需要依據上面提到的五點規範,同時因爲規則一、二、3基本都會知足,下面咱們主要討論規則四、5。假設咱們這裏有一棵最簡單的樹,咱們規定新增的節點爲N、它的父節點爲P、P的兄弟節點爲U、P的父節點爲G。
對於新節點的插入有以下三個關鍵地方:
一、插入新節點老是紅色節點 。
二、若是插入節點的父節點是黑色, 能維持性質 。
三、若是插入節點的父節點是紅色, 破壞了性質. 故插入算法就是經過從新着色或旋轉, 來維持性質 。
爲了保證下面的闡述更加清晰和根據便於參考,我這裏將紅黑樹的五點規定再貼一遍:
一、每一個節點都只能是紅色或者黑色
二、根節點是黑色
三、每一個葉節點(NIL節點,空節點)是黑色的。
四、若是一個結點是紅的,則它兩個子節點都是黑的。也就是說在一條路徑上不能出現相鄰的兩個紅色結點。
五、從任一節點到其每一個葉子的全部路徑都包含相同數目的黑色節點。
若新插入的節點N沒有父節點,則直接當作根據節點插入便可,同時將顏色設置爲黑色。(如圖一(1))
2、父節點爲黑色
這種狀況新節點N一樣是直接插入,同時顏色爲紅色,因爲根據規則四它會存在兩個黑色的葉子節點,值爲null。同時因爲新增節點N爲紅色,因此經過它的子節點的路徑依然會保存着相同的黑色節點數,一樣知足規則5。(如圖一(2))
(圖一)
3、若父節點P和P的兄弟節點U都爲紅色
對於這種狀況若直接插入確定會出現不平衡現象。怎麼處理?P、U節點變黑、G節點變紅。這時因爲通過節點P、U的路徑都必須通過G因此在這些路徑上面的黑節點數目仍是相同的。可是通過上面的處理,可能G節點的父節點也是紅色,這個時候咱們須要將G節點當作新增節點遞歸處理。
對於這種狀況咱們對新增節點N、P進行一次左旋轉。這裏所產生的結果其實並無完成,還不是平衡的(違反了規則四),這是咱們須要進行狀況5的操做。
5、父節點P爲紅色,叔父節點U爲黑色或者缺乏,新增節點N爲父節點P左孩子
這種狀況有多是因爲狀況四而產生的,也有可能不是。對於這種狀況先已P節點爲中心進行右旋轉,在旋轉後產生的樹中,節點P是節點N、G的父節點。可是這棵樹並不規範,它違反了規則4,因此咱們將P、G節點的顏色進行交換,使之其知足規範。開始時全部的路徑都須要通過G其餘們的黑色節點數同樣,可是如今全部的路徑改成通過P,且P爲整棵樹的惟一黑色節點,因此調整後的樹一樣知足規範5。
上面展現了紅黑樹新增節點的五種狀況,這五種狀況涵蓋了全部的新增可能,無論這棵紅黑樹多麼複雜,均可以根據這五種狀況來進行生成。下面就來分析Java中的TreeMap是如何來實現紅黑樹的。