写在前面
A Red-Black tree based NavigableMa implementation.
TreeMap,是一种具有排序功能的 Map
,是一种基于红黑树结构的的 NavigableMap
。由于它具有红黑树的结构,所以它的 get
、 put
、 constainsKey
、remove
等操作能够保证在log(n)的时间复杂度。
红黑树
TreeMap是基于红黑树实现的一种 NavigableMap
。红黑树是一种二叉搜索树,具有以下性质:
性质1
树上的节点要么是红色要么是黑色性质2
根节点一定是黑色性质3
树上不存在连续的红色节点性质4
任一根到叶子节点路径上具有相同数目的黑色节点
无论是往红黑树上添加节点还是移除节点,都需要进行必要的balance,以保证以上四条性质不被破坏。
往树上添加节点和移除节点的balance策略是不尽相同的,各自都需要分多钟情况讨论。
在对这两种情况展开讨论之前,先说明两个操作,即 左旋
和 右旋
:
左旋
如上图,对G左旋,G原本的右孩子U,成为G的父节点,G成为U的左孩子,U原本的左孩子Ul成为G的右孩子。
右旋
如上图,对G右旋,P成为父节点,G为P的右孩子,P原本的右孩子Pr,挪为G的左孩子。
添加节点
节点上的节点要么是红色要么是黑色,那么添加的节点应该选择哪种颜色呢?答案应该是红色,应为如果是黑色,那么 性质4
就被破坏了,所以为了避免复杂性,应该设定添加的节点颜色为红色。
假设要往树上 P
下添加节点 N
, P
的兄弟节点为 U
, 父节点为 G
。如图:
以上图为例,在插入节点时做以下情况说明:
情况一
N 是头结点。则从red变色为black情况二
N 的父节点P是黑色。则不用调整情况三
N 的父节点P是红色,叔叔节点U也为红色。P、U -> black; then G -> red;可能需要往上递归处理,因为可能存在连续的红色节点,如果G是root,则需要将其颜色置为black情况四
N 的父节点P是红色,叔叔节点U为黑色,且N是P的左孩子。则P、G互换颜色,然后对G右旋情况五
N 的父节点P是红色,叔叔节点U为黑色,且N为P的右孩子。则对P先左旋,然后按情况四
处理
插入的情况一共就以上五种情况,可以自己画一画加深印象。
移除节点
移除节点的流程一般是这样: 直接删除,找到前驱(删除节点左子树上的最大节点)或后继(删除节点右子树上的最小节点),将找到的前驱或后继赋值到删除节点,最后删除前驱或者后继,因为前驱或后继至多只有一个孩子,因此问题简化为删除至多只有一个孩子的问题。
移除节点的情况就先对复杂一些了,以上图为例分情况说明:
情况一
移除节点 X 为red。则由唯一的孩子顶替即可(红黑树的四个原则并没有遭到破坏)情况二
删除节点是black,path上少了个black节点,分为两种大的情况:s1
: 待删除节点唯一的孩子为红色,则直接顶替并变色即可s2
: 待删除节点唯一的孩子为黑色,需要分为6中情况(以上图例子说明):s2.1
:删除节点为根节点,则子节点直接替换父节点s2.2
:N 父节点G为red,兄弟节点s及其孩子均为black,少了个black x,即N路径上少了个黑,则父节点和兄弟节点换个颜色s2.3
:N 的父节点G可red可black,兄弟节点s为black,s的左孩子SL为可red可black,s的右孩子SR为red,则1.对G左旋;2.再G和S互换颜色,SL移为N的右孩子,SR改变颜色为blacks2.4
:N 的父节点G可red可black,兄弟节点s为black,s的左孩子SL为red,s的右孩子SR为black,则对S右旋,且S和SL互换颜色,则变成情况3s2.5
:N 的兄弟节点S为red,其他节点为黑色,则G和S互换颜色, 对G左旋,这个时候左子树(照具体情况按情况2 3 4处理)s2.6
:N 的父节点,兄弟节点及其兄弟孩子节点均为black,则将S变为red,这时按情况5处理
源码分析
TreeMap的类声明如下:
1 | public class TreeMap<K, V> |
可知TreeMap具有Map的一般行为,同是也是NavigableMap。
变量
private final Comparator<? super K> comparator;
// 用于元素排序的比较器private transient Entry<K, V> root;
// 红黑树的rootprivate transient int size = 0;
// 目前红黑树中容纳的元素个数private transient int modCount = 0;
// 修改计数,failfastprivate static final boolean RED = false;
// 红色private static final boolean BLACK = true;
// 黑色构造方法
1 | public TreeMap() { |
默认构造方法并没有初始化任何变量,包括树的首尾(并不需要一般链表中的dummy node)
1 | public TreeMap(Comparator<? super K> comparator) { |
带一个维护内部元素顺序的比较器参数
1 | public TreeMap(SortedMap<K, ? extends V> m) { |
要是传入的是一个已经有序的Map,那么可以根据这个Map迭代构造红黑树
1 | public TreeMap(java.util.Map<? extends K, ? extends V> m) { |
要是Map是个有的Map,则在构造时建立红黑树(同上一情况),否则等到第一次对树上元素操作时才会处理
一般行为
rotateLeft
左旋
1 | private void rotateLeft(Entry<K, V> p) { |
rotateRight
右旋
1 | private void rotateRight(Entry<K, V> p) { |
predecessor
前驱
1 | static <K, V> Entry<K, V> predecessor(Entry<K, V> t) { |
successor
后继
1 | static <K, V> Entry<K, V> successor(Entry<K, V> t) { |
getLastEntry
1 | final Entry<K, V> getLastEntry() { |
getFirstEntry
1 | final Entry<K, V> getFirstEntry() { |
getEntry
1 | final Entry<K, V> getEntry(Object key) { |
put
1 | public V put(K key, V value) { |
remove
1 | public V remove(Object key) { |
小结
- TreeMap为一种有序的Map
- 有序的保证来自于内部元素的组织依托于红黑树
- 因为内部是红黑树实现,所以
put
get
等操作的时间复杂度为log(n)