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是如何来实现红黑树的。