[转载] RED-BLACK(红黑)树的实现TreeMap源码阅读

转载自http://lxy2330.iteye.com/blog/1664786

由于平衡二叉树与红黑树都是二叉排序树,又红黑树是对平衡二叉树的一种改进实现,所以它的很多思想算法都来源于排序二叉或平衡二叉树,比如排序二叉树中的添加、删除、查找及查找直接后继节点等,平衡二叉树中的左旋与右旋等都是一样的,所以当看到这些方法时,要多参考以下两节:《二叉排序(搜索)树实现 》与《平衡二叉树实现 》

SortedMap 接口的基于红黑树的实现。此类保证了Map按照升序顺序排列关键字,根据使用的构造方法不同,可能会按照键的类的自然顺序进行排序(Comparable),或者按照创建时所提供的比较(Comparator)进行排序。此实现不是同步的,多个线程同时访问一个同一个map时,一般通过对封装该map的对象进行同步操作来完成,或者使用  Map m = Collections.synchronizedMap(new TreeMap(...)) 方法来包装该map

RED-BLACK树的性质:
1、根元素永远是显色。
2、除根节点外,所有新插入的节点都是红色的。

3、Red规则:红色节点的子节点都是黑色的,不能有两个红色节点相邻。
4、Path规则:从根元素到某个子节点或是只有一个子节点的元素的所有路径中,黑色元素的个数必须相同。

Java代码  

  1. package tree.redblack;
  2. import java.util.AbstractCollection;
  3. import java.util.AbstractMap;
  4. import java.util.AbstractSet;
  5. import java.util.Collection;
  6. import java.util.Comparator;
  7. import java.util.ConcurrentModificationException;
  8. import java.util.Iterator;
  9. import java.util.Map;
  10. import java.util.NoSuchElementException;
  11. import java.util.Set;
  12. import java.util.SortedMap;
  13. public class TreeMap extends AbstractMap implements SortedMap, Cloneable,
  14. java.io.Serializable {
  15. //比较器,用于TreeMap的key的排序,如果为null,则使用key的自然排序法
  16. private Comparator comparator = null;
  17. private transient Entry root = null;//根节点
  18. private transient int size = 0;//树中的节点数
  19. /*
  20. * TreeMap结构被修改的次数,注:结构上修改是指添加或删除一个或多个映射关系的操作, 仅改变与
  21. * 现有键关联的值不是结构上的修改。
  22. *
  23. * 由所有此类的 collection 视图方法(如keySet、values、entrySet三个方法) 所返回的迭代
  24. * 器都是快速失效的:在迭代器创建之后,如果对map修改了结构,除非通过迭代器自身的 remove 或
  25. *  add 方法,其他map自身任何时间任何方式的修改,迭代器都将抛出 ConcurrentModification
  26. *  Exception。因此,面对并发的修改,迭代器很快就完全失效,而不是冒着在将来在不确定的时间点
  27. *  上发生不确定问题的风险。
  28. */
  29. private transient int modCount = 0;
  30. //增加节点后modCount与size都需递增
  31. private void incrementSize() {
  32. modCount++;
  33. size++;
  34. }
  35. private void decrementSize() {
  36. modCount++;
  37. size--;
  38. }
  39. /**
  40. * 默认构造器,构造一个空的map,根据key的自然比较排序。所有插入到map中的节点的key必须实现
  41. * 过自然比较器Comparable接口。
  42. */
  43. public TreeMap() {
  44. }
  45. /**
  46. * 构造一个新的空映射,该映射根据给定的比较器进行排序
  47. */
  48. public TreeMap(Comparator c) {
  49. this.comparator = c;
  50. }
  51. /**
  52. * 构造一个新map,所含的元素与给定的map相同,这个新map按照键的自然顺序进行关键字排序
  53. */
  54. public TreeMap(Map m) {
  55. putAll(m);
  56. }
  57. /**
  58. * 构造一个新的映射,所含的元素与给定的map相同,该映射按照SortedMap相同的排序方式进行排序
  59. */
  60. public TreeMap(SortedMap m) {
  61. comparator = m.comparator();
  62. //...
  63. }
  64. public int size() {
  65. return size;
  66. }
  67. public boolean containsKey(Object key) {
  68. return getEntry(key) != null;
  69. }
  70. public boolean containsValue(Object value) {
  71. return (root == null ? false : (value == null ? valueSearchNull(root)
  72. : valueSearchNonNull(root, value)));
  73. }
  74. //树中是否有value域值为null的节点
  75. private boolean valueSearchNull(Entry n) {
  76. if (n.value == null)
  77. return true;
  78. // 递归在左右子树中查找
  79. return (n.left != null && valueSearchNull(n.left))
  80. || (n.right != null && valueSearchNull(n.right));
  81. }
  82. /*
  83. * 查找指定节点的value值域的节点,因为树是按节的key关键字来排序的,而不是按value排序的,
  84. * 所以在找value时在最坏的情况下遍历整个树
  85. *
  86. * 以二叉树的先序遍历查找
  87. */
  88. private boolean valueSearchNonNull(Entry n, Object value) {
  89. // 先比较根
  90. if (value.equals(n.value))
  91. return true;
  92. // 再比较左,最后再比较右
  93. return (n.left != null && valueSearchNonNull(n.left, value))
  94. || (n.right != null && valueSearchNonNull(n.right, value));
  95. }
  96. //获取指定key的值
  97. public Object get(Object key) {
  98. Entry p = getEntry(key);
  99. return (p == null ? null : p.value);
  100. }
  101. //返回树中第一个(最小的)键,最左边的节点
  102. public Object firstKey() {
  103. return key(firstEntry());
  104. }
  105. /**
  106. * Returns the last (highest) key currently in this sorted map.
  107. *
  108. * @return the last (highest) key currently in this sorted map.
  109. * @throws    NoSuchElementException Map is empty.
  110. */
  111. public Object lastKey() {
  112. return key(lastEntry());
  113. }
  114. /*
  115. * 根据给定的key查找节点,因为 RED-BLACK树也是一种二叉排序树,所以采用在二叉排序树
  116. * 中查找节点的算法来查找
  117. */
  118. private Entry getEntry(Object key) {
  119. Entry p = root;
  120. while (p != null) {
  121. int cmp = compare(key, p.key);
  122. if (cmp == 0)
  123. return p;//找到则返回
  124. else if (cmp < 0)
  125. p = p.left;//如果关键字小于当前节点,则在当前节点的左子树中找
  126. else
  127. p = p.right;//如果关键字大于当前节点,则在当前节点的右子树中找
  128. }
  129. return null;
  130. }
  131. private static Object key(Entry e) {
  132. if (e == null)
  133. throw new NoSuchElementException();
  134. return e.key;
  135. }
  136. /**
  137. * 如果key已存在,将替换原值,并返回原来key所对应的值;如果不存在,则返回 null(注,如果
  138. * 新增key已存在,且为null,返回时也会为null,value为null并不代表key不存在)
  139. */
  140. public Object put(Object key, Object value) {
  141. //插入根节点时不需要调整颜色
  142. Entry t = root;
  143. if (t == null) {
  144. incrementSize();
  145. root = new Entry(key, value, null);
  146. return null;
  147. }
  148. while (true) {
  149. int cmp = compare(key, t.key);
  150. if (cmp == 0) {
  151. return t.setValue(value);
  152. } else if (cmp < 0) {
  153. if (t.left != null) {
  154. t = t.left;
  155. } else {
  156. incrementSize();
  157. t.left = new Entry(key, value, t);
  158. //需要调整颜色
  159. fixAfterInsertion(t.left);
  160. return null;
  161. }
  162. } else { // cmp > 0
  163. if (t.right != null) {
  164. t = t.right;
  165. } else {
  166. incrementSize();
  167. t.right = new Entry(key, value, t);
  168. //需要调整颜色
  169. fixAfterInsertion(t.right);
  170. return null;
  171. }
  172. }
  173. }
  174. }
  175. //从map中删除指定的key节点,并返回value,如果不存在或value本身就是null时,返回null
  176. public Object remove(Object key) {
  177. Entry p = getEntry(key);
  178. if (p == null)
  179. return null;
  180. Object oldValue = p.value;
  181. deleteEntry(p);
  182. return oldValue;
  183. }
  184. public void clear() {
  185. modCount++;
  186. size = 0;
  187. root = null;
  188. }
  189. public Object clone() {
  190. TreeMap clone = null;
  191. try {
  192. //调整父类的克隆方法
  193. clone = (TreeMap) super.clone();
  194. } catch (CloneNotSupportedException e) {
  195. throw new InternalError();
  196. }
  197. // Put clone into "virgin" state (except for comparator)
  198. clone.root = null;
  199. clone.size = 0;
  200. clone.modCount = 0;
  201. clone.entrySet = null;
  202. //...
  203. return clone;
  204. }
  205. // Views
  206. /**
  207. * key-value(键-值对)视图,即该set视图里是一个个的Entry实体节点
  208. */
  209. private transient volatile Set entrySet = null;
  210. //以下两个字段是从父类AbstractMap拷贝过来的,因为在该类与父类不在一个包中,所以不可见
  211. transient volatile Set keySet = null;//key视图,set视图里是一个个Entry实体节点的key
  212. //value视图,collection视图里是一个个Entry实体节点的value
  213. transient volatile Collection values = null;
  214. public Set keySet() {
  215. if (keySet == null) {
  216. //keySet视图实现
  217. keySet = new AbstractSet() {
  218. public Iterator iterator() {
  219. //只能对key进行迭代
  220. return new KeyIterator();
  221. }
  222. public int size() {
  223. return TreeMap.this.size();
  224. }
  225. //...
  226. };
  227. }
  228. return keySet;
  229. }
  230. //value视图中的value排列的顺序保持不变,实质上还是在entrySet视图上迭代的
  231. public Collection values() {
  232. if (values == null) {
  233. //value视图实现
  234. values = new AbstractCollection() {
  235. public Iterator iterator() {
  236. //只能迭代value
  237. return new ValueIterator();
  238. }
  239. public int size() {
  240. return TreeMap.this.size();
  241. }
  242. //...
  243. };
  244. }
  245. return values;
  246. }
  247. //key-value 视图,即Entry节点视图
  248. public Set entrySet() {
  249. if (entrySet == null) {
  250. entrySet = new AbstractSet() {
  251. public Iterator iterator() {
  252. //对Entry节点迭代
  253. return new EntryIterator();
  254. }
  255. //...
  256. public int size() {
  257. return TreeMap.this.size();
  258. }
  259. };
  260. }
  261. return entrySet;
  262. }
  263. //Entry迭代器
  264. private class EntryIterator implements Iterator {
  265. private int expectedModCount = TreeMap.this.modCount;
  266. private Entry lastReturned = null;//指向最后一次next操作所返回的节点
  267. Entry next;//指向当前next()操作要返回的节点
  268. EntryIterator() {
  269. next = firstEntry();//开始时next指向最左边的节点,即中序遍历序列中的第一个节点
  270. }
  271. // Used by SubMapEntryIterator
  272. EntryIterator(Entry first) {
  273. next = first;
  274. }
  275. public boolean hasNext() {
  276. //如果next没有指向null,则表示当前next指向的节点还有
  277. return next != null;
  278. }
  279. //KeyIterator与ValueIterator两迭代器的next()方法实现都是调整此方法来实现的
  280. final Entry nextEntry() {
  281. if (next == null)
  282. throw new NoSuchElementException();
  283. if (modCount != expectedModCount)
  284. throw new ConcurrentModificationException();
  285. lastReturned = next;//先取
  286. next = successor(next);//next再下移
  287. return lastReturned;
  288. }
  289. public Object next() {
  290. //对Entry进行迭代
  291. return nextEntry();
  292. }
  293. public void remove() {
  294. if (lastReturned == null)
  295. throw new IllegalStateException();
  296. if (modCount != expectedModCount)
  297. throw new ConcurrentModificationException();
  298. //如果删除的节点左右子树都存在,则删除后next指针需后退到lastReturned的位置,
  299. //具体为什么,请参考二叉搜索树的实现 BinSearchTree.java 相应方法
  300. if (lastReturned.left != null && lastReturned.right != null)
  301. next = lastReturned;
  302. deleteEntry(lastReturned);
  303. expectedModCount++;
  304. lastReturned = null;
  305. }
  306. }
  307. //KeyIterator是在EntryIterator的基础上只对key进行迭代罢了
  308. private class KeyIterator extends EntryIterator {
  309. public Object next() {
  310. return nextEntry().key;
  311. }
  312. }
  313. //ValueIterator是在EntryIterator的基础上只对value进行迭代罢了
  314. private class ValueIterator extends EntryIterator {
  315. public Object next() {
  316. return nextEntry().value;
  317. }
  318. }
  319. //在这里决定是使用 Comparator 还 Comparable 比较器
  320. private int compare(Object k1, Object k2) {
  321. return (comparator == null ? ((Comparable) k1).compareTo(k2) : comparator
  322. .compare(k1, k2));
  323. }
  324. private static boolean valEquals(Object o1, Object o2) {
  325. return (o1 == null ? o2 == null : o1.equals(o2));
  326. }
  327. private static final boolean RED = false;//false代表红
  328. private static final boolean BLACK = true;//true代表黑
  329. /**
  330. * 树中的节点结构体实现,实现了Map.Entry接口,它是双向的,即可以从子找到父,也可从
  331. * 父到子节点
  332. */
  333. static class Entry implements Map.Entry {
  334. Object key;//数据域key
  335. Object value;//数据域value
  336. Entry left = null;//左指针
  337. Entry right = null;//右指针
  338. Entry parent;//父指针
  339. boolean color = BLACK;//节点着色,相当于平衡二叉排序树中的平衡因子
  340. Entry(Object key, Object value, Entry parent) {
  341. this.key = key;
  342. this.value = value;
  343. this.parent = parent;
  344. }
  345. public Object getKey() {
  346. return key;
  347. }
  348. public Object getValue() {
  349. return value;
  350. }
  351. public Object setValue(Object value) {
  352. Object oldValue = this.value;
  353. this.value = value;
  354. return oldValue;
  355. }
  356. //...
  357. public String toString() {
  358. return key + "=" + value;
  359. }
  360. }
  361. //取树中最左边的节点,即key最小的节点
  362. private Entry firstEntry() {
  363. Entry p = root;
  364. if (p != null)
  365. while (p.left != null)
  366. p = p.left;
  367. return p;
  368. }
  369. //取树中最右边的节点,即key最大的节点
  370. private Entry lastEntry() {
  371. Entry p = root;
  372. if (p != null)
  373. while (p.right != null)
  374. p = p.right;
  375. return p;
  376. }
  377. //查找某节点的中序遍历的直接后继节点,具体请参见BinSearchTree.java 相应方法
  378. private Entry successor(Entry t) {
  379. if (t == null)
  380. return null;
  381. else if (t.right != null) {
  382. Entry p = t.right;
  383. while (p.left != null)
  384. p = p.left;
  385. return p;
  386. } else {
  387. Entry p = t.parent;
  388. Entry ch = t;
  389. while (p != null && ch == p.right) {
  390. ch = p;
  391. p = p.parent;
  392. }
  393. return p;
  394. }
  395. }
  396. //平衡操作
  397. private static boolean colorOf(Entry p) {
  398. //注,如果为null,则返回为黑色,即如果求某个节点的子节点颜色,但子节点不存在时也会返回黑色
  399. return (p == null ? BLACK : p.color);
  400. }
  401. private static Entry parentOf(Entry p) {
  402. return (p == null ? null : p.parent);
  403. }
  404. private static void setColor(Entry p, boolean c) {
  405. if (p != null)
  406. p.color = c;
  407. }
  408. private static Entry leftOf(Entry p) {
  409. return (p == null) ? null : p.left;
  410. }
  411. private static Entry rightOf(Entry p) {
  412. return (p == null) ? null : p.right;
  413. }
  414. //左旋,具体请参见AVLTree.java相应方法
  415. private void rotateLeft(Entry p) {
  416. Entry r = p.right;
  417. p.right = r.left;
  418. if (r.left != null)
  419. r.left.parent = p;
  420. r.parent = p.parent;
  421. if (p.parent == null)
  422. root = r;
  423. else if (p.parent.left == p)
  424. p.parent.left = r;
  425. else
  426. p.parent.right = r;
  427. r.left = p;
  428. p.parent = r;
  429. }
  430. //右旋,具体请参见AVLTree.java相应方法
  431. private void rotateRight(Entry p) {
  432. Entry l = p.left;
  433. p.left = l.right;
  434. if (l.right != null)
  435. l.right.parent = p;
  436. l.parent = p.parent;
  437. if (p.parent == null)
  438. root = l;
  439. else if (p.parent.right == p)
  440. p.parent.right = l;
  441. else
  442. p.parent.left = l;
  443. l.right = p;
  444. p.parent = l;
  445. }
  446. /** From CLR **/
  447. private void fixAfterInsertion(Entry x) {
  448. //插入的节点为红色
  449. x.color = RED;
  450. /*
  451. * 添加节点时,因为在上面将新增节点设置成了红色,所以添加后是不会打破 Path规则 的,但是
  452. * 有可能会打破 Red规则,又打破 Red规则 的前提是父节点是红色的才可能,所以这里的循环条
  453. * 件就是父节点必须是红色的才需调整
  454. *
  455. * 1、如果x为根,则不需要重新染色与旋转,因为最后会将根再次染成BLACK
  456. * 2、如果x的父节点color为黑色,也没必要进行重排了,因为red规则没有被破坏
  457. * 如果上面两个条件不满足,则一直循环,这里x != null 条件也是不可少的,传递进来的值
  458. * 与循环终止时都能可能为null
  459. */
  460. while (x != null && x != root && x.parent.color == RED) {
  461. //一、x的父节点处于左子节点时
  462. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  463. //y指向x的右叔,注,有可能为NULL
  464. Entry y = rightOf(parentOf(parentOf(x)));
  465. //如果y为红色
  466. if (colorOf(y) == RED) {//如果条件成立,则y不能可为空
  467. /*
  468. * 情况1:colorOf(y) == RED
  469. *
  470. * 此情况下y不可能为null,因为 colorOf(null) == BLACK,所以此情况
  471. * 下右叔一定存在,既然右叔是红色,所以爷爷节点50只能是黑色了,否则打破
  472. * 了Red规则了。30也肯定是红色了,因为进行while循环前提条件就是父节点
  473. * 一定是红色的
  474. *                         着色
  475. *           .              →              .
  476. *           .                             .
  477. *           50B                      x → 50R
  478. *           /\                            /\
  479. *        30R  90R ← y(右叔)             30B  90B ← y
  480. *           \                            \
  481. *      x → 40R                          40R
  482. *
  483. * 此情况下不需要旋转,完成着色后继续循环
  484. */
  485. //将x节点的父节点的着黑
  486. setColor(parentOf(x), BLACK);
  487. //将x的右叔的着黑
  488. setColor(y, BLACK);
  489. //将x的爷爷着红
  490. setColor(parentOf(parentOf(x)), RED);
  491. /*
  492. * 完成操作后,我们发现x向上移动了两层,因此循环的最大次数是树的高度的一
  493. * 半,这也就是TreeMap中的put方法的最大执行时间为 O(logn) 原因所在了
  494. */
  495. x = parentOf(parentOf(x));//再将x指向爷爷
  496. }//如果y为黑色
  497. else {
  498. if (x == rightOf(parentOf(x))) {
  499. /*
  500. * 情况2:colorOf(y) == BLACK,且x为父的右子节点
  501. *
  502. * 此情况下,y一定是null,为什么?因为插入的节点40的父节点30为红色
  503. * (又为什么?因为进行while循环的前提就是父为红色啊),所以30的父
  504. * 节点只能为黑色了(又为什么?因为如果是红色的话,早就打破了Red规则
  505. * 了),现进入到此分支时前提是y必须是黑色的,如果y真的存在,则Path
  506. * 规则又将被打破,所以此情况下的y一定是null,这样才满足Path规则
  507. *
  508. * ====实质上此种情况就是将问题2转换成转换成问题3来操作====
  509. *
  510. *                   绕30左旋
  511. *        .            →             .
  512. *        .                          .
  513. *       50B                        50B
  514. *        /                          /
  515. *      30R  (Y为NULL)             40R
  516. *        \                         /
  517. *   x → 40R                   x → 30R
  518. */
  519. x = parentOf(x);
  520. rotateLeft(x);
  521. }
  522. /*
  523. * 情况3:colorOf(y) == BLACK,且x为父的左子节点
  524. *
  525. * 如果x为父的右子节点,经过上面情况2的处理,已经将其转换成情况3了,此种
  526. * 情况下的y也一定为null,与第2种情况道理是一样的
  527. *
  528. *                    着色               绕50右旋
  529. *        .            →         .       →       .
  530. *        .                      .               .
  531. *       50B                    50R            40B
  532. *        /                      /               /\
  533. *      40R  (Y为NULL)          40B        x → 30R 50R
  534. *       /                      /
  535. * x → 30R                 x → 30R
  536. *
  537. * 处理后,下次循环自动会终止,因为此情况处理后x的父节点总是黑色的,所以
  538. * 循环一定会终止于下次
  539. */
  540. setColor(parentOf(x), BLACK);//x的父着黑
  541. setColor(parentOf(parentOf(x)), RED);//x的爷着红
  542. if (parentOf(parentOf(x)) != null)
  543. rotateRight(parentOf(parentOf(x)));//绕爷爷右旋
  544. }
  545. }//二、x的父节点处于右子节点时,与第一种对称
  546. else {
  547. //x的左叔
  548. Entry y = leftOf(parentOf(parentOf(x)));
  549. //如果左叔为红色
  550. if (colorOf(y) == RED) {
  551. /*
  552. * 情况1:colorOf(y) == RED,即左叔为红色
  553. *
  554. * 此情况下y不可能为null,因为 colorOf(null) == BLACK,所以此情况
  555. * 下左叔一定存在,既然左叔是红色,所以爷爷节点50只能是黑色了,否则打破
  556. * 了Red规则了。90也肯定是红色了,因为进行while循环前提条件就是父节点
  557. * 一定是红色的
  558. *                         着色
  559. *           .              →              .
  560. *           .                             .
  561. *           50B                      x → 50R
  562. *           /\                            /\
  563. * y(左叔)→30R  90R                      30B 90B ← y
  564. *              \                             \
  565. *          x → 100R                          100R
  566. *
  567. * 此情况下不需要旋转,完成着色后继续循环
  568. */
  569. setColor(parentOf(x), BLACK);
  570. setColor(y, BLACK);
  571. setColor(parentOf(parentOf(x)), RED);
  572. /*
  573. * 完成操作后,我们发现x向上移动了两层,因此循环的最大次数是树的高度的一
  574. * 半,这也就是TreeMap中的put方法的最大执行时间为 O(logn) 原因所在了
  575. */
  576. x = parentOf(parentOf(x));//再将x指向爷爷
  577. }//如果y为黑色
  578. else {
  579. if (x == leftOf(parentOf(x))) {
  580. /*
  581. * 情况2:colorOf(y) == BLACK(左叔为黑),且x为父的左子节点
  582. *
  583. * 此情况下,y一定是null,为什么?因为插入的节点60的父节点90为红色
  584. * ,所以90的父节点只能为黑色了,现进入到此分支时前提是y必须是黑色的
  585. * ,如果y真的存在,则Path规则又将被打破,所以此情况下的y一定是null
  586. * ,这样才满足Path规则
  587. *
  588. * ====实质上此种情况就是将问题2转换成转换成问题3来操作====
  589. *
  590. *                   绕90右旋
  591. *          .            →             .
  592. *          .                          .
  593. *          50B                        50B
  594. *           \                          \
  595. *(Y为NULL)  90R                        60R
  596. *           /                           \
  597. *      x → 60R                      x → 90R
  598. */
  599. x = parentOf(x);
  600. rotateRight(x);
  601. }
  602. /*
  603. * 情况3:colorOf(y) == BLACK,且x为父的右子节点
  604. *
  605. * 如果x为父的左子节点,经过上面情况2的处理,已经将其转换成情况3了,此种
  606. * 情况下的y也一定为null,与第2种情况道理是一样的
  607. *
  608. *                    着色               绕50左旋
  609. *         .            →         .       →       .
  610. *         .                      .               .
  611. *        50B                    50R            90B
  612. *         \                      \               /\
  613. *(Y为NULL) 90R                   90B       x → 50R 95R
  614. *           \                     \
  615. *       x → 95R               x → 95R
  616. *
  617. * 处理后,下次循环自动会终止,因为此情况处理后x的父节点总是黑色的,所以
  618. * 循环一定会终止于下次
  619. */
  620. setColor(parentOf(x), BLACK);//x的父着黑
  621. setColor(parentOf(parentOf(x)), RED);//x的爷着红
  622. if (parentOf(parentOf(x)) != null)
  623. rotateLeft(parentOf(parentOf(x)));//绕爷爷左旋
  624. }
  625. }
  626. }
  627. root.color = BLACK;//不管怎样调整,最后根节点都要着黑
  628. }
  629. //删除节点p,然后重新调整
  630. private void deleteEntry(Entry p) {
  631. decrementSize();
  632. /*
  633. * 如果删除的节点p有左右子树时,将问题转换成删除叶子节点或只有一个子节点的节点问题,具体
  634. * 过程:使用中序遍历直接后s继的数据域值(key与value)拷贝到p的相应数据域值,然后将p指
  635. * 向直接后继s,这样待删除的节点就变成了s,问题已转换成删除叶节点或只有一个子节点的问题了
  636. */
  637. if (p.left != null && p.right != null) {
  638. Entry s = successor(p);
  639. p.key = s.key;
  640. p.value = s.value;
  641. p = s;
  642. }
  643. //replacement用来替换即将被删除节点p
  644. Entry replacement = (p.left != null ? p.left : p.right);
  645. //如果待删除的元素有子节点(如果有,则也有且仅有一个子节点)
  646. if (replacement != null) {
  647. // 重新设置替换节点的父
  648. replacement.parent = p.parent;
  649. //如果待删除节点的为根节点
  650. if (p.parent == null)
  651. root = replacement;//则替换元素将成为根
  652. else if (p == p.parent.left)
  653. //否则如果待删除节点为左节点,则使待删除节点的父的左指针指向替换节点
  654. p.parent.left = replacement;
  655. else
  656. //否则如果待删除节点为右节点,则使待删除节点的父的右指针指向替换节点
  657. p.parent.right = replacement;
  658. // 解除p与其他节点的关系
  659. p.left = p.right = p.parent = null;
  660. /*
  661. * 因为被删除的节点只有两种节点: 叶子节点、只有一个子节点的节点
  662. *
  663. * 如果删除的是只有一个子节点的节点时,根据路径规则,被删除节点一定是黑色的,而子节点
  664. * 一定是红色的,这时删除操作过程就是删除了一个黑红节点而在删除位置上又放置了一个红色
  665. * 节点,因此,删除一个有一个子节点的节点时,Red与Path规则都有可能打破
  666. */
  667. if (p.color == BLACK)//???这里的条件好像是多余的,因为p一定是黑,难道另有玄机?
  668. //删除后从替换节点replacement位置向根方向调整
  669. fixAfterDeletion(replacement);
  670. } else if (p.parent == null) { // 如果待删除节点为根,则置root为null
  671. root = null;
  672. } else { // 如果删除的是叶子节点
  673. /*
  674. * 如果删除的是叶子节点,不管该叶子节点是红还是黑,则 Red规则 在删除后不可能被打破,
  675. * 所以此情况下只有可能打破 Path规则 ,但打破的前提是被删除的节点是黑色的,因为只有
  676. * 黑色节点才影响Path路径规则,所以调整的前提是 p.color == BLACK,即被删除的叶
  677. * 节点是黑色的
  678. */
  679. if (p.color == BLACK)
  680. //删除前从即将被删除节点p位置向根方向调整
  681. fixAfterDeletion(p);
  682. //???此条件好像多余,因为如果不成立,则走上面那个分支了,难道另藏玄机
  683. if (p.parent != null) {
  684. if (p == p.parent.left)
  685. p.parent.left = null;
  686. else if (p == p.parent.right)
  687. p.parent.right = null;
  688. p.parent = null;//解除p与其他节点的关系
  689. }
  690. }
  691. }
  692. //删除节点后调整
  693. private void fixAfterDeletion(Entry x) {
  694. //直循环到根节点或红色止
  695. while (x != root && colorOf(x) == BLACK) {
  696. //x为左子节点
  697. if (x == leftOf(parentOf(x))) {
  698. //x的同胞
  699. Entry sib = rightOf(parentOf(x));//有可能为null
  700. if (colorOf(sib) == RED) {//同胞不可能为null
  701. /*
  702. * 情况1:colorOf(sib) == RED,即右叔存在且为红
  703. *              通过执行上面deleteEntry后,x、p均指向后继65
  704. *        p → 60B             →           65B
  705. *            /  \                        /  \
  706. *          50B   70B                   50B   70B
  707. *          /\    /  \                  /\    /  \
  708. *       45B 55B 65B 80R             45B 55B 65B 80R ← sib
  709. *                   / \                     ↑   /  \
  710. *                 75B 87B                 x、p 75B 87B
  711. *  着色                         绕70左旋
  712. *   →        65B               →           65B
  713. *            /  \                          /  \
  714. *          50B   70R                     50B   80B
  715. *          /\    /  \                    /\    / \
  716. *       45B 55B 65B 80B ← sib         45B 55B 70R 87B
  717. *                ↑   / \                      /  \
  718. *              x、p 75B 87B            x、p → 65B 75B ← sib
  719. *  x现有一个新的同胞节点,该节点为黑,现将情况1转向其他3种情况
  720. */
  721. setColor(sib, BLACK);//置同胞为黑
  722. setColor(parentOf(x), RED);//置父为红
  723. rotateLeft(parentOf(x));//左旋
  724. sib = rightOf(parentOf(x));//再处理同胞节点
  725. }
  726. if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {
  727. /*
  728. * 情况2:
  729. * colorOf(leftOf(sib))==BLACK && colorOf(rightOf(sib))==BLACK
  730. * 即同胞不存在或同胞存在时为黑且没有子节点的情况
  731. *
  732. * 现接着上面情况1结束后的那棵树处理
  733. * 着色                       再将x指向父(70R)继续处理
  734. *  →      65B                   →           ...
  735. *         /  \
  736. *       50B   80B
  737. *       /\     / \
  738. *     45B 55B 70R 87B
  739. *             / \
  740. *     x、p → 65B 75R ← sib
  741. *
  742. * 处理完后,x为红,因此结束循环,然后设置x的颜色为黑,并从此返回
  743. * deleteEntry方法,此时p所指向的节点已被从其父节点解除链接
  744. */
  745. setColor(sib, RED);
  746. x = parentOf(x);
  747. } else {
  748. if (colorOf(rightOf(sib)) == BLACK) {
  749. /*
  750. * 情况3:经过情况2后,此时同胞节点左子节点必为红
  751. *
  752. * 假设在开始while循环之前有如下树:
  753. *  →      65B
  754. *         /  \
  755. *       50B   80R
  756. *      / \    /  \
  757. *    45B 55B 70B  90B
  758. *            / \   / \
  759. *     x → 65B 75B 85R 95B
  760. *               / \
  761. *          sib  82B 87B
  762. *
  763. * 情况2被应用,因此将75的颜色设置为红,并令 x = parentOf(x)后第
  764. * 一遍循环结束。第二遍循环开始时,会应用情况3,此时同胞节点为90,树
  765. * 如下图:
  766. * 第一次循环完                  着色
  767. *    →    65B                →          65B
  768. *        /   \                          /  \
  769. *      50B    80R                    50B    80R
  770. *     / \     /  \                   / \    /  \
  771. *   45B 55B 70B   90B ← sib       45B 55B 70B   90R ← sib
  772. *         / \    / \                  / \     / \
  773. *       x 65B 75R 85R 95B             x 65B 75R 85B 95B
  774. *                 / \                           / \
  775. *               82B 87B                       82B 87B
  776. *
  777. * 绕90右旋后并修改sib指向           进行下一步循环,应用情况4
  778. *    →          65B                      → ...
  779. *              /   \
  780. *            50B    80R
  781. *           / \     /  \
  782. *         45B 55B 70B   85B ← sib
  783. *               / \    / \
  784. *             x 65B 75R 82B 90R
  785. *                           / \
  786. *                         87B 95B
  787. *
  788. * 一旦情况3被应用,则同胞节点的右子节点将设置为 RED,因此情况4在情况
  789. * 3之后应用,为了不在下一次循环中应用情况4,直接将情况4的代码放在了
  790. * 情况3的后面
  791. */
  792. setColor(leftOf(sib), BLACK);
  793. setColor(sib, RED);
  794. rotateRight(sib);
  795. sib = rightOf(parentOf(x));
  796. }
  797. /*
  798. * 情况4:经过情况3后剩下的情况4,此时同胞节点的右子节点是红
  799. *
  800. * 接着第3种情况处理:
  801. *
  802. *   着色                    绕80左旋
  803. *    →       65B            →              65B
  804. *           /   \                          /  \
  805. *         50B    80B                     50B   85R ← sib
  806. *        / \     /  \                   / \    /  \
  807. *      45B 55B 70B   85R ← sib        45B 55B 80B 90B
  808. *            / \    / \                     / \   \
  809. *          x 65B 75R 82B 90B            x → 70B 87B 95B
  810. *                        / \                 /
  811. *                      87B 95B         p → 65B
  812. *
  813. * 循环结束后返回到 deleteEntry 方法,删除65节点即完成删除操作过程
  814. */
  815. setColor(sib, colorOf(parentOf(x)));
  816. setColor(parentOf(x), BLACK);
  817. setColor(rightOf(sib), BLACK);
  818. rotateLeft(parentOf(x));
  819. //情况3与4被应用后,x将被设置为根节点,同时,这也是循环的最后一次
  820. x = root;
  821. }
  822. } else { // 与上面对称
  823. Entry sib = leftOf(parentOf(x));
  824. if (colorOf(sib) == RED) {
  825. setColor(sib, BLACK);
  826. setColor(parentOf(x), RED);
  827. rotateRight(parentOf(x));
  828. sib = leftOf(parentOf(x));
  829. }
  830. if (colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK) {
  831. setColor(sib, RED);
  832. x = parentOf(x);
  833. } else {
  834. if (colorOf(leftOf(sib)) == BLACK) {
  835. setColor(rightOf(sib), BLACK);
  836. setColor(sib, RED);
  837. rotateLeft(sib);
  838. sib = leftOf(parentOf(x));
  839. }
  840. setColor(sib, colorOf(parentOf(x)));
  841. setColor(parentOf(x), BLACK);
  842. setColor(leftOf(sib), BLACK);
  843. rotateRight(parentOf(x));
  844. x = root;
  845. }
  846. }
  847. }
  848. //退出循环时x为根或为红色,直接将其值为黑色即可
  849. setColor(x, BLACK);
  850. }
  851. }

Java代码  

  1. package tree.redblack;
  2. import java.util.AbstractCollection;
  3. import java.util.AbstractMap;
  4. import java.util.AbstractSet;
  5. import java.util.Collection;
  6. import java.util.Comparator;
  7. import java.util.ConcurrentModificationException;
  8. import java.util.Iterator;
  9. import java.util.Map;
  10. import java.util.NoSuchElementException;
  11. import java.util.Set;
  12. import java.util.SortedMap;
  13. public class TreeMap extends AbstractMap implements SortedMap, Cloneable,
  14. java.io.Serializable {
  15. //比较器,用于TreeMap的key的排序,如果为null,则使用key的自然排序法
  16. private Comparator comparator = null;
  17. private transient Entry root = null;//根节点
  18. private transient int size = 0;//树中的节点数
  19. /*
  20. * TreeMap结构被修改的次数,注:结构上修改是指添加或删除一个或多个映射关系的操作, 仅改变与
  21. * 现有键关联的值不是结构上的修改。
  22. *
  23. * 由所有此类的 collection 视图方法(如keySet、values、entrySet三个方法) 所返回的迭代
  24. * 器都是快速失效的:在迭代器创建之后,如果对map修改了结构,除非通过迭代器自身的 remove 或
  25. *  add 方法,其他map自身任何时间任何方式的修改,迭代器都将抛出 ConcurrentModification
  26. *  Exception。因此,面对并发的修改,迭代器很快就完全失效,而不是冒着在将来在不确定的时间点
  27. *  上发生不确定问题的风险。
  28. */
  29. private transient int modCount = 0;
  30. //增加节点后modCount与size都需递增
  31. private void incrementSize() {
  32. modCount++;
  33. size++;
  34. }
  35. private void decrementSize() {
  36. modCount++;
  37. size--;
  38. }
  39. /**
  40. * 默认构造器,构造一个空的map,根据key的自然比较排序。所有插入到map中的节点的key必须实现
  41. * 过自然比较器Comparable接口。
  42. */
  43. public TreeMap() {
  44. }
  45. /**
  46. * 构造一个新的空映射,该映射根据给定的比较器进行排序
  47. */
  48. public TreeMap(Comparator c) {
  49. this.comparator = c;
  50. }
  51. /**
  52. * 构造一个新map,所含的元素与给定的map相同,这个新map按照键的自然顺序进行关键字排序
  53. */
  54. public TreeMap(Map m) {
  55. putAll(m);
  56. }
  57. /**
  58. * 构造一个新的映射,所含的元素与给定的map相同,该映射按照SortedMap相同的排序方式进行排序
  59. */
  60. public TreeMap(SortedMap m) {
  61. comparator = m.comparator();
  62. //...
  63. }
  64. public int size() {
  65. return size;
  66. }
  67. public boolean containsKey(Object key) {
  68. return getEntry(key) != null;
  69. }
  70. public boolean containsValue(Object value) {
  71. return (root == null ? false : (value == null ? valueSearchNull(root)
  72. : valueSearchNonNull(root, value)));
  73. }
  74. //树中是否有value域值为null的节点
  75. private boolean valueSearchNull(Entry n) {
  76. if (n.value == null)
  77. return true;
  78. // 递归在左右子树中查找
  79. return (n.left != null && valueSearchNull(n.left))
  80. || (n.right != null && valueSearchNull(n.right));
  81. }
  82. /*
  83. * 查找指定节点的value值域的节点,因为树是按节的key关键字来排序的,而不是按value排序的,
  84. * 所以在找value时在最坏的情况下遍历整个树
  85. *
  86. * 以二叉树的先序遍历查找
  87. */
  88. private boolean valueSearchNonNull(Entry n, Object value) {
  89. // 先比较根
  90. if (value.equals(n.value))
  91. return true;
  92. // 再比较左,最后再比较右
  93. return (n.left != null && valueSearchNonNull(n.left, value))
  94. || (n.right != null && valueSearchNonNull(n.right, value));
  95. }
  96. //获取指定key的值
  97. public Object get(Object key) {
  98. Entry p = getEntry(key);
  99. return (p == null ? null : p.value);
  100. }
  101. //返回树中第一个(最小的)键,最左边的节点
  102. public Object firstKey() {
  103. return key(firstEntry());
  104. }
  105. /**
  106. * Returns the last (highest) key currently in this sorted map.
  107. *
  108. * @return the last (highest) key currently in this sorted map.
  109. * @throws    NoSuchElementException Map is empty.
  110. */
  111. public Object lastKey() {
  112. return key(lastEntry());
  113. }
  114. /*
  115. * 根据给定的key查找节点,因为 RED-BLACK树也是一种二叉排序树,所以采用在二叉排序树
  116. * 中查找节点的算法来查找
  117. */
  118. private Entry getEntry(Object key) {
  119. Entry p = root;
  120. while (p != null) {
  121. int cmp = compare(key, p.key);
  122. if (cmp == 0)
  123. return p;//找到则返回
  124. else if (cmp < 0)
  125. p = p.left;//如果关键字小于当前节点,则在当前节点的左子树中找
  126. else
  127. p = p.right;//如果关键字大于当前节点,则在当前节点的右子树中找
  128. }
  129. return null;
  130. }
  131. private static Object key(Entry e) {
  132. if (e == null)
  133. throw new NoSuchElementException();
  134. return e.key;
  135. }
  136. /**
  137. * 如果key已存在,将替换原值,并返回原来key所对应的值;如果不存在,则返回 null(注,如果
  138. * 新增key已存在,且为null,返回时也会为null,value为null并不代表key不存在)
  139. */
  140. public Object put(Object key, Object value) {
  141. //插入根节点时不需要调整颜色
  142. Entry t = root;
  143. if (t == null) {
  144. incrementSize();
  145. root = new Entry(key, value, null);
  146. return null;
  147. }
  148. while (true) {
  149. int cmp = compare(key, t.key);
  150. if (cmp == 0) {
  151. return t.setValue(value);
  152. } else if (cmp < 0) {
  153. if (t.left != null) {
  154. t = t.left;
  155. } else {
  156. incrementSize();
  157. t.left = new Entry(key, value, t);
  158. //需要调整颜色
  159. fixAfterInsertion(t.left);
  160. return null;
  161. }
  162. } else { // cmp > 0
  163. if (t.right != null) {
  164. t = t.right;
  165. } else {
  166. incrementSize();
  167. t.right = new Entry(key, value, t);
  168. //需要调整颜色
  169. fixAfterInsertion(t.right);
  170. return null;
  171. }
  172. }
  173. }
  174. }
  175. //从map中删除指定的key节点,并返回value,如果不存在或value本身就是null时,返回null
  176. public Object remove(Object key) {
  177. Entry p = getEntry(key);
  178. if (p == null)
  179. return null;
  180. Object oldValue = p.value;
  181. deleteEntry(p);
  182. return oldValue;
  183. }
  184. public void clear() {
  185. modCount++;
  186. size = 0;
  187. root = null;
  188. }
  189. public Object clone() {
  190. TreeMap clone = null;
  191. try {
  192. //调整父类的克隆方法
  193. clone = (TreeMap) super.clone();
  194. } catch (CloneNotSupportedException e) {
  195. throw new InternalError();
  196. }
  197. // Put clone into "virgin" state (except for comparator)
  198. clone.root = null;
  199. clone.size = 0;
  200. clone.modCount = 0;
  201. clone.entrySet = null;
  202. //...
  203. return clone;
  204. }
  205. // Views
  206. /**
  207. * key-value(键-值对)视图,即该set视图里是一个个的Entry实体节点
  208. */
  209. private transient volatile Set entrySet = null;
  210. //以下两个字段是从父类AbstractMap拷贝过来的,因为在该类与父类不在一个包中,所以不可见
  211. transient volatile Set keySet = null;//key视图,set视图里是一个个Entry实体节点的key
  212. //value视图,collection视图里是一个个Entry实体节点的value
  213. transient volatile Collection values = null;
  214. public Set keySet() {
  215. if (keySet == null) {
  216. //keySet视图实现
  217. keySet = new AbstractSet() {
  218. public Iterator iterator() {
  219. //只能对key进行迭代
  220. return new KeyIterator();
  221. }
  222. public int size() {
  223. return TreeMap.this.size();
  224. }
  225. //...
  226. };
  227. }
  228. return keySet;
  229. }
  230. //value视图中的value排列的顺序保持不变,实质上还是在entrySet视图上迭代的
  231. public Collection values() {
  232. if (values == null) {
  233. //value视图实现
  234. values = new AbstractCollection() {
  235. public Iterator iterator() {
  236. //只能迭代value
  237. return new ValueIterator();
  238. }
  239. public int size() {
  240. return TreeMap.this.size();
  241. }
  242. //...
  243. };
  244. }
  245. return values;
  246. }
  247. //key-value 视图,即Entry节点视图
  248. public Set entrySet() {
  249. if (entrySet == null) {
  250. entrySet = new AbstractSet() {
  251. public Iterator iterator() {
  252. //对Entry节点迭代
  253. return new EntryIterator();
  254. }
  255. //...
  256. public int size() {
  257. return TreeMap.this.size();
  258. }
  259. };
  260. }
  261. return entrySet;
  262. }
  263. //Entry迭代器
  264. private class EntryIterator implements Iterator {
  265. private int expectedModCount = TreeMap.this.modCount;
  266. private Entry lastReturned = null;//指向最后一次next操作所返回的节点
  267. Entry next;//指向当前next()操作要返回的节点
  268. EntryIterator() {
  269. next = firstEntry();//开始时next指向最左边的节点,即中序遍历序列中的第一个节点
  270. }
  271. // Used by SubMapEntryIterator
  272. EntryIterator(Entry first) {
  273. next = first;
  274. }
  275. public boolean hasNext() {
  276. //如果next没有指向null,则表示当前next指向的节点还有
  277. return next != null;
  278. }
  279. //KeyIterator与ValueIterator两迭代器的next()方法实现都是调整此方法来实现的
  280. final Entry nextEntry() {
  281. if (next == null)
  282. throw new NoSuchElementException();
  283. if (modCount != expectedModCount)
  284. throw new ConcurrentModificationException();
  285. lastReturned = next;//先取
  286. next = successor(next);//next再下移
  287. return lastReturned;
  288. }
  289. public Object next() {
  290. //对Entry进行迭代
  291. return nextEntry();
  292. }
  293. public void remove() {
  294. if (lastReturned == null)
  295. throw new IllegalStateException();
  296. if (modCount != expectedModCount)
  297. throw new ConcurrentModificationException();
  298. //如果删除的节点左右子树都存在,则删除后next指针需后退到lastReturned的位置,
  299. //具体为什么,请参考二叉搜索树的实现 BinSearchTree.java 相应方法
  300. if (lastReturned.left != null && lastReturned.right != null)
  301. next = lastReturned;
  302. deleteEntry(lastReturned);
  303. expectedModCount++;
  304. lastReturned = null;
  305. }
  306. }
  307. //KeyIterator是在EntryIterator的基础上只对key进行迭代罢了
  308. private class KeyIterator extends EntryIterator {
  309. public Object next() {
  310. return nextEntry().key;
  311. }
  312. }
  313. //ValueIterator是在EntryIterator的基础上只对value进行迭代罢了
  314. private class ValueIterator extends EntryIterator {
  315. public Object next() {
  316. return nextEntry().value;
  317. }
  318. }
  319. //在这里决定是使用 Comparator 还 Comparable 比较器
  320. private int compare(Object k1, Object k2) {
  321. return (comparator == null ? ((Comparable) k1).compareTo(k2) : comparator
  322. .compare(k1, k2));
  323. }
  324. private static boolean valEquals(Object o1, Object o2) {
  325. return (o1 == null ? o2 == null : o1.equals(o2));
  326. }
  327. private static final boolean RED = false;//false代表红
  328. private static final boolean BLACK = true;//true代表黑
  329. /**
  330. * 树中的节点结构体实现,实现了Map.Entry接口,它是双向的,即可以从子找到父,也可从
  331. * 父到子节点
  332. */
  333. static class Entry implements Map.Entry {
  334. Object key;//数据域key
  335. Object value;//数据域value
  336. Entry left = null;//左指针
  337. Entry right = null;//右指针
  338. Entry parent;//父指针
  339. boolean color = BLACK;//节点着色,相当于平衡二叉排序树中的平衡因子
  340. Entry(Object key, Object value, Entry parent) {
  341. this.key = key;
  342. this.value = value;
  343. this.parent = parent;
  344. }
  345. public Object getKey() {
  346. return key;
  347. }
  348. public Object getValue() {
  349. return value;
  350. }
  351. public Object setValue(Object value) {
  352. Object oldValue = this.value;
  353. this.value = value;
  354. return oldValue;
  355. }
  356. //...
  357. public String toString() {
  358. return key + "=" + value;
  359. }
  360. }
  361. //取树中最左边的节点,即key最小的节点
  362. private Entry firstEntry() {
  363. Entry p = root;
  364. if (p != null)
  365. while (p.left != null)
  366. p = p.left;
  367. return p;
  368. }
  369. //取树中最右边的节点,即key最大的节点
  370. private Entry lastEntry() {
  371. Entry p = root;
  372. if (p != null)
  373. while (p.right != null)
  374. p = p.right;
  375. return p;
  376. }
  377. //查找某节点的中序遍历的直接后继节点,具体请参见BinSearchTree.java 相应方法
  378. private Entry successor(Entry t) {
  379. if (t == null)
  380. return null;
  381. else if (t.right != null) {
  382. Entry p = t.right;
  383. while (p.left != null)
  384. p = p.left;
  385. return p;
  386. } else {
  387. Entry p = t.parent;
  388. Entry ch = t;
  389. while (p != null && ch == p.right) {
  390. ch = p;
  391. p = p.parent;
  392. }
  393. return p;
  394. }
  395. }
  396. //平衡操作
  397. private static boolean colorOf(Entry p) {
  398. //注,如果为null,则返回为黑色,即如果求某个节点的子节点颜色,但子节点不存在时也会返回黑色
  399. return (p == null ? BLACK : p.color);
  400. }
  401. private static Entry parentOf(Entry p) {
  402. return (p == null ? null : p.parent);
  403. }
  404. private static void setColor(Entry p, boolean c) {
  405. if (p != null)
  406. p.color = c;
  407. }
  408. private static Entry leftOf(Entry p) {
  409. return (p == null) ? null : p.left;
  410. }
  411. private static Entry rightOf(Entry p) {
  412. return (p == null) ? null : p.right;
  413. }
  414. //左旋,具体请参见AVLTree.java相应方法
  415. private void rotateLeft(Entry p) {
  416. Entry r = p.right;
  417. p.right = r.left;
  418. if (r.left != null)
  419. r.left.parent = p;
  420. r.parent = p.parent;
  421. if (p.parent == null)
  422. root = r;
  423. else if (p.parent.left == p)
  424. p.parent.left = r;
  425. else
  426. p.parent.right = r;
  427. r.left = p;
  428. p.parent = r;
  429. }
  430. //右旋,具体请参见AVLTree.java相应方法
  431. private void rotateRight(Entry p) {
  432. Entry l = p.left;
  433. p.left = l.right;
  434. if (l.right != null)
  435. l.right.parent = p;
  436. l.parent = p.parent;
  437. if (p.parent == null)
  438. root = l;
  439. else if (p.parent.right == p)
  440. p.parent.right = l;
  441. else
  442. p.parent.left = l;
  443. l.right = p;
  444. p.parent = l;
  445. }
  446. /** From CLR **/
  447. private void fixAfterInsertion(Entry x) {
  448. //插入的节点为红色
  449. x.color = RED;
  450. /*
  451. * 添加节点时,因为在上面将新增节点设置成了红色,所以添加后是不会打破 Path规则 的,但是
  452. * 有可能会打破 Red规则,又打破 Red规则 的前提是父节点是红色的才可能,所以这里的循环条
  453. * 件就是父节点必须是红色的才需调整
  454. *
  455. * 1、如果x为根,则不需要重新染色与旋转,因为最后会将根再次染成BLACK
  456. * 2、如果x的父节点color为黑色,也没必要进行重排了,因为red规则没有被破坏
  457. * 如果上面两个条件不满足,则一直循环,这里x != null 条件也是不可少的,传递进来的值
  458. * 与循环终止时都能可能为null
  459. */
  460. while (x != null && x != root && x.parent.color == RED) {
  461. //一、x的父节点处于左子节点时
  462. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  463. //y指向x的右叔,注,有可能为NULL
  464. Entry y = rightOf(parentOf(parentOf(x)));
  465. //如果y为红色
  466. if (colorOf(y) == RED) {//如果条件成立,则y不能可为空
  467. /*
  468. * 情况1:colorOf(y) == RED
  469. *
  470. * 此情况下y不可能为null,因为 colorOf(null) == BLACK,所以此情况
  471. * 下右叔一定存在,既然右叔是红色,所以爷爷节点50只能是黑色了,否则打破
  472. * 了Red规则了。30也肯定是红色了,因为进行while循环前提条件就是父节点
  473. * 一定是红色的
  474. *                         着色
  475. *           .              →              .
  476. *           .                             .
  477. *           50B                      x → 50R
  478. *           /\                            /\
  479. *        30R  90R ← y(右叔)             30B  90B ← y
  480. *           \                            \
  481. *      x → 40R                          40R
  482. *
  483. * 此情况下不需要旋转,完成着色后继续循环
  484. */
  485. //将x节点的父节点的着黑
  486. setColor(parentOf(x), BLACK);
  487. //将x的右叔的着黑
  488. setColor(y, BLACK);
  489. //将x的爷爷着红
  490. setColor(parentOf(parentOf(x)), RED);
  491. /*
  492. * 完成操作后,我们发现x向上移动了两层,因此循环的最大次数是树的高度的一
  493. * 半,这也就是TreeMap中的put方法的最大执行时间为 O(logn) 原因所在了
  494. */
  495. x = parentOf(parentOf(x));//再将x指向爷爷
  496. }//如果y为黑色
  497. else {
  498. if (x == rightOf(parentOf(x))) {
  499. /*
  500. * 情况2:colorOf(y) == BLACK,且x为父的右子节点
  501. *
  502. * 此情况下,y一定是null,为什么?因为插入的节点40的父节点30为红色
  503. * (又为什么?因为进行while循环的前提就是父为红色啊),所以30的父
  504. * 节点只能为黑色了(又为什么?因为如果是红色的话,早就打破了Red规则
  505. * 了),现进入到此分支时前提是y必须是黑色的,如果y真的存在,则Path
  506. * 规则又将被打破,所以此情况下的y一定是null,这样才满足Path规则
  507. *
  508. * ====实质上此种情况就是将问题2转换成转换成问题3来操作====
  509. *
  510. *                   绕30左旋
  511. *        .            →             .
  512. *        .                          .
  513. *       50B                        50B
  514. *        /                          /
  515. *      30R  (Y为NULL)             40R
  516. *        \                         /
  517. *   x → 40R                   x → 30R
  518. */
  519. x = parentOf(x);
  520. rotateLeft(x);
  521. }
  522. /*
  523. * 情况3:colorOf(y) == BLACK,且x为父的左子节点
  524. *
  525. * 如果x为父的右子节点,经过上面情况2的处理,已经将其转换成情况3了,此种
  526. * 情况下的y也一定为null,与第2种情况道理是一样的
  527. *
  528. *                    着色               绕50右旋
  529. *        .            →         .       →       .
  530. *        .                      .               .
  531. *       50B                    50R            40B
  532. *        /                      /               /\
  533. *      40R  (Y为NULL)          40B        x → 30R 50R
  534. *       /                      /
  535. * x → 30R                 x → 30R
  536. *
  537. * 处理后,下次循环自动会终止,因为此情况处理后x的父节点总是黑色的,所以
  538. * 循环一定会终止于下次
  539. */
  540. setColor(parentOf(x), BLACK);//x的父着黑
  541. setColor(parentOf(parentOf(x)), RED);//x的爷着红
  542. if (parentOf(parentOf(x)) != null)
  543. rotateRight(parentOf(parentOf(x)));//绕爷爷右旋
  544. }
  545. }//二、x的父节点处于右子节点时,与第一种对称
  546. else {
  547. //x的左叔
  548. Entry y = leftOf(parentOf(parentOf(x)));
  549. //如果左叔为红色
  550. if (colorOf(y) == RED) {
  551. /*
  552. * 情况1:colorOf(y) == RED,即左叔为红色
  553. *
  554. * 此情况下y不可能为null,因为 colorOf(null) == BLACK,所以此情况
  555. * 下左叔一定存在,既然左叔是红色,所以爷爷节点50只能是黑色了,否则打破
  556. * 了Red规则了。90也肯定是红色了,因为进行while循环前提条件就是父节点
  557. * 一定是红色的
  558. *                         着色
  559. *           .              →              .
  560. *           .                             .
  561. *           50B                      x → 50R
  562. *           /\                            /\
  563. * y(左叔)→30R  90R                      30B 90B ← y
  564. *              \                             \
  565. *          x → 100R                          100R
  566. *
  567. * 此情况下不需要旋转,完成着色后继续循环
  568. */
  569. setColor(parentOf(x), BLACK);
  570. setColor(y, BLACK);
  571. setColor(parentOf(parentOf(x)), RED);
  572. /*
  573. * 完成操作后,我们发现x向上移动了两层,因此循环的最大次数是树的高度的一
  574. * 半,这也就是TreeMap中的put方法的最大执行时间为 O(logn) 原因所在了
  575. */
  576. x = parentOf(parentOf(x));//再将x指向爷爷
  577. }//如果y为黑色
  578. else {
  579. if (x == leftOf(parentOf(x))) {
  580. /*
  581. * 情况2:colorOf(y) == BLACK(左叔为黑),且x为父的左子节点
  582. *
  583. * 此情况下,y一定是null,为什么?因为插入的节点60的父节点90为红色
  584. * ,所以90的父节点只能为黑色了,现进入到此分支时前提是y必须是黑色的
  585. * ,如果y真的存在,则Path规则又将被打破,所以此情况下的y一定是null
  586. * ,这样才满足Path规则
  587. *
  588. * ====实质上此种情况就是将问题2转换成转换成问题3来操作====
  589. *
  590. *                   绕90右旋
  591. *          .            →             .
  592. *          .                          .
  593. *          50B                        50B
  594. *           \                          \
  595. *(Y为NULL)  90R                        60R
  596. *           /                           \
  597. *      x → 60R                      x → 90R
  598. */
  599. x = parentOf(x);
  600. rotateRight(x);
  601. }
  602. /*
  603. * 情况3:colorOf(y) == BLACK,且x为父的右子节点
  604. *
  605. * 如果x为父的左子节点,经过上面情况2的处理,已经将其转换成情况3了,此种
  606. * 情况下的y也一定为null,与第2种情况道理是一样的
  607. *
  608. *                    着色               绕50左旋
  609. *         .            →         .       →       .
  610. *         .                      .               .
  611. *        50B                    50R            90B
  612. *         \                      \               /\
  613. *(Y为NULL) 90R                   90B       x → 50R 95R
  614. *           \                     \
  615. *       x → 95R               x → 95R
  616. *
  617. * 处理后,下次循环自动会终止,因为此情况处理后x的父节点总是黑色的,所以
  618. * 循环一定会终止于下次
  619. */
  620. setColor(parentOf(x), BLACK);//x的父着黑
  621. setColor(parentOf(parentOf(x)), RED);//x的爷着红
  622. if (parentOf(parentOf(x)) != null)
  623. rotateLeft(parentOf(parentOf(x)));//绕爷爷左旋
  624. }
  625. }
  626. }
  627. root.color = BLACK;//不管怎样调整,最后根节点都要着黑
  628. }
  629. //删除节点p,然后重新调整
  630. private void deleteEntry(Entry p) {
  631. decrementSize();
  632. /*
  633. * 如果删除的节点p有左右子树时,将问题转换成删除叶子节点或只有一个子节点的节点问题,具体
  634. * 过程:使用中序遍历直接后s继的数据域值(key与value)拷贝到p的相应数据域值,然后将p指
  635. * 向直接后继s,这样待删除的节点就变成了s,问题已转换成删除叶节点或只有一个子节点的问题了
  636. */
  637. if (p.left != null && p.right != null) {
  638. Entry s = successor(p);
  639. p.key = s.key;
  640. p.value = s.value;
  641. p = s;
  642. }
  643. //replacement用来替换即将被删除节点p
  644. Entry replacement = (p.left != null ? p.left : p.right);
  645. //如果待删除的元素有子节点(如果有,则也有且仅有一个子节点)
  646. if (replacement != null) {
  647. // 重新设置替换节点的父
  648. replacement.parent = p.parent;
  649. //如果待删除节点的为根节点
  650. if (p.parent == null)
  651. root = replacement;//则替换元素将成为根
  652. else if (p == p.parent.left)
  653. //否则如果待删除节点为左节点,则使待删除节点的父的左指针指向替换节点
  654. p.parent.left = replacement;
  655. else
  656. //否则如果待删除节点为右节点,则使待删除节点的父的右指针指向替换节点
  657. p.parent.right = replacement;
  658. // 解除p与其他节点的关系
  659. p.left = p.right = p.parent = null;
  660. /*
  661. * 因为被删除的节点只有两种节点: 叶子节点、只有一个子节点的节点
  662. *
  663. * 如果删除的是只有一个子节点的节点时,根据路径规则,被删除节点一定是黑色的,而子节点
  664. * 一定是红色的,这时删除操作过程就是删除了一个黑红节点而在删除位置上又放置了一个红色
  665. * 节点,因此,删除一个有一个子节点的节点时,Red与Path规则都有可能打破
  666. */
  667. if (p.color == BLACK)//???这里的条件好像是多余的,因为p一定是黑,难道另有玄机?
  668. //删除后从替换节点replacement位置向根方向调整
  669. fixAfterDeletion(replacement);
  670. } else if (p.parent == null) { // 如果待删除节点为根,则置root为null
  671. root = null;
  672. } else { // 如果删除的是叶子节点
  673. /*
  674. * 如果删除的是叶子节点,不管该叶子节点是红还是黑,则 Red规则 在删除后不可能被打破,
  675. * 所以此情况下只有可能打破 Path规则 ,但打破的前提是被删除的节点是黑色的,因为只有
  676. * 黑色节点才影响Path路径规则,所以调整的前提是 p.color == BLACK,即被删除的叶
  677. * 节点是黑色的
  678. */
  679. if (p.color == BLACK)
  680. //删除前从即将被删除节点p位置向根方向调整
  681. fixAfterDeletion(p);
  682. //???此条件好像多余,因为如果不成立,则走上面那个分支了,难道另藏玄机
  683. if (p.parent != null) {
  684. if (p == p.parent.left)
  685. p.parent.left = null;
  686. else if (p == p.parent.right)
  687. p.parent.right = null;
  688. p.parent = null;//解除p与其他节点的关系
  689. }
  690. }
  691. }
  692. //删除节点后调整
  693. private void fixAfterDeletion(Entry x) {
  694. //直循环到根节点或红色止
  695. while (x != root && colorOf(x) == BLACK) {
  696. //x为左子节点
  697. if (x == leftOf(parentOf(x))) {
  698. //x的同胞
  699. Entry sib = rightOf(parentOf(x));//有可能为null
  700. if (colorOf(sib) == RED) {//同胞不可能为null
  701. /*
  702. * 情况1:colorOf(sib) == RED,即右叔存在且为红
  703. *              通过执行上面deleteEntry后,x、p均指向后继65
  704. *        p → 60B             →           65B
  705. *            /  \                        /  \
  706. *          50B   70B                   50B   70B
  707. *          /\    /  \                  /\    /  \
  708. *       45B 55B 65B 80R             45B 55B 65B 80R ← sib
  709. *                   / \                     ↑   /  \
  710. *                 75B 87B                 x、p 75B 87B
  711. *  着色                         绕70左旋
  712. *   →        65B               →           65B
  713. *            /  \                          /  \
  714. *          50B   70R                     50B   80B
  715. *          /\    /  \                    /\    / \
  716. *       45B 55B 65B 80B ← sib         45B 55B 70R 87B
  717. *                ↑   / \                      /  \
  718. *              x、p 75B 87B            x、p → 65B 75B ← sib
  719. *  x现有一个新的同胞节点,该节点为黑,现将情况1转向其他3种情况
  720. */
  721. setColor(sib, BLACK);//置同胞为黑
  722. setColor(parentOf(x), RED);//置父为红
  723. rotateLeft(parentOf(x));//左旋
  724. sib = rightOf(parentOf(x));//再处理同胞节点
  725. }
  726. if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {
  727. /*
  728. * 情况2:
  729. * colorOf(leftOf(sib))==BLACK && colorOf(rightOf(sib))==BLACK
  730. * 即同胞不存在或同胞存在时为黑且没有子节点的情况
  731. *
  732. * 现接着上面情况1结束后的那棵树处理
  733. * 着色                       再将x指向父(70R)继续处理
  734. *  →      65B                   →           ...
  735. *         /  \
  736. *       50B   80B
  737. *       /\     / \
  738. *     45B 55B 70R 87B
  739. *             / \
  740. *     x、p → 65B 75R ← sib
  741. *
  742. * 处理完后,x为红,因此结束循环,然后设置x的颜色为黑,并从此返回
  743. * deleteEntry方法,此时p所指向的节点已被从其父节点解除链接
  744. */
  745. setColor(sib, RED);
  746. x = parentOf(x);
  747. } else {
  748. if (colorOf(rightOf(sib)) == BLACK) {
  749. /*
  750. * 情况3:经过情况2后,此时同胞节点左子节点必为红
  751. *
  752. * 假设在开始while循环之前有如下树:
  753. *  →      65B
  754. *         /  \
  755. *       50B   80R
  756. *      / \    /  \
  757. *    45B 55B 70B  90B
  758. *            / \   / \
  759. *     x → 65B 75B 85R 95B
  760. *               / \
  761. *          sib  82B 87B
  762. *
  763. * 情况2被应用,因此将75的颜色设置为红,并令 x = parentOf(x)后第
  764. * 一遍循环结束。第二遍循环开始时,会应用情况3,此时同胞节点为90,树
  765. * 如下图:
  766. * 第一次循环完                  着色
  767. *    →    65B                →          65B
  768. *        /   \                          /  \
  769. *      50B    80R                    50B    80R
  770. *     / \     /  \                   / \    /  \
  771. *   45B 55B 70B   90B ← sib       45B 55B 70B   90R ← sib
  772. *         / \    / \                  / \     / \
  773. *       x 65B 75R 85R 95B             x 65B 75R 85B 95B
  774. *                 / \                           / \
  775. *               82B 87B                       82B 87B
  776. *
  777. * 绕90右旋后并修改sib指向           进行下一步循环,应用情况4
  778. *    →          65B                      → ...
  779. *              /   \
  780. *            50B    80R
  781. *           / \     /  \
  782. *         45B 55B 70B   85B ← sib
  783. *               / \    / \
  784. *             x 65B 75R 82B 90R
  785. *                           / \
  786. *                         87B 95B
  787. *
  788. * 一旦情况3被应用,则同胞节点的右子节点将设置为 RED,因此情况4在情况
  789. * 3之后应用,为了不在下一次循环中应用情况4,直接将情况4的代码放在了
  790. * 情况3的后面
  791. */
  792. setColor(leftOf(sib), BLACK);
  793. setColor(sib, RED);
  794. rotateRight(sib);
  795. sib = rightOf(parentOf(x));
  796. }
  797. /*
  798. * 情况4:经过情况3后剩下的情况4,此时同胞节点的右子节点是红
  799. *
  800. * 接着第3种情况处理:
  801. *
  802. *   着色                    绕80左旋
  803. *    →       65B            →              65B
  804. *           /   \                          /  \
  805. *         50B    80B                     50B   85R ← sib
  806. *        / \     /  \                   / \    /  \
  807. *      45B 55B 70B   85R ← sib        45B 55B 80B 90B
  808. *            / \    / \                     / \   \
  809. *          x 65B 75R 82B 90B            x → 70B 87B 95B
  810. *                        / \                 /
  811. *                      87B 95B         p → 65B
  812. *
  813. * 循环结束后返回到 deleteEntry 方法,删除65节点即完成删除操作过程
  814. */
  815. setColor(sib, colorOf(parentOf(x)));
  816. setColor(parentOf(x), BLACK);
  817. setColor(rightOf(sib), BLACK);
  818. rotateLeft(parentOf(x));
  819. //情况3与4被应用后,x将被设置为根节点,同时,这也是循环的最后一次
  820. x = root;
  821. }
  822. } else { // 与上面对称
  823. Entry sib = leftOf(parentOf(x));
  824. if (colorOf(sib) == RED) {
  825. setColor(sib, BLACK);
  826. setColor(parentOf(x), RED);
  827. rotateRight(parentOf(x));
  828. sib = leftOf(parentOf(x));
  829. }
  830. if (colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK) {
  831. setColor(sib, RED);
  832. x = parentOf(x);
  833. } else {
  834. if (colorOf(leftOf(sib)) == BLACK) {
  835. setColor(rightOf(sib), BLACK);
  836. setColor(sib, RED);
  837. rotateLeft(sib);
  838. sib = leftOf(parentOf(x));
  839. }
  840. setColor(sib, colorOf(parentOf(x)));
  841. setColor(parentOf(x), BLACK);
  842. setColor(leftOf(sib), BLACK);
  843. rotateRight(parentOf(x));
  844. x = root;
  845. }
  846. }
  847. }
  848. //退出循环时x为根或为红色,直接将其值为黑色即可
  849. setColor(x, BLACK);
  850. }
  851. }
时间: 2024-10-08 06:58:04

[转载] RED-BLACK(红黑)树的实现TreeMap源码阅读的相关文章

结合java.util.TreeMap源码理解红黑树

前言 本篇将结合JDK1.6的TreeMap源码,来一起探索红-黑树的奥秘.红黑树是解决二叉搜索树的非平衡问题. 当插入(或者删除)一个新节点时,为了使树保持平衡,必须遵循一定的规则,这个规则就是红-黑规则: 1) 每个节点不是红色的就是黑色的 2) 根总是黑色的 3) 如果节点是红色的,则它的子节点必须是黑色的(反之倒不一定必须为真) 4) 从跟到叶节点或者空子节点的每条路径,必须包含相同数目的黑色节点 插入一个新节点 红-黑树的插入过程和普通的二叉搜索树基本一致:从跟朝插入点位置走,在每个节

死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 删除元素 删除元素本身比较简单,就是采用二叉树的删除规则. (1)如果删除的位置有两个叶子节点,则从其右子树中取最小的元素放到删除的位置,然后把删除位置移到替代元素的位置,进入下一步. (2)如果删除的位置只有一个叶子节点(有可能是经过第一步转换后的删除位置),则把那个叶子节点作为替代元素,放到删除的位置,然后把这个叶子节点删除. (3)如果删除的位置没有叶子节点,则直接把这个删除位置的元素删除即可.

TreeMap源码分析之一 —— 排序二叉树、平衡二叉树、红黑树

一.排序二叉树(BST树) 1.排序二叉树的定义 排序二叉树,Binary Sort Tree 排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树: (1)若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值: (2)若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值: (3)它的左.右子树也分别为排序二叉树. 按中序遍历排序二叉树可以得到由小到大的有序序列. 比如 2.排序二叉树的插入和删除 二.平衡二叉树(Balanced Binary Tree,AVL树) 三.红黑

现代编译原理--第六章(中间树 IR Tree 含源码)

(转载请表明出处   http://www.cnblogs.com/BlackWalnut/p/4559717.html ) 这一章,就虎书而言,理论知识点是及其少的,就介绍了为什么要有一个中间表示树.看下面这张图就能理解为什么了. 但是章的代码量却是挺多的.在写代码之前,如果不懂整个代码的布局,是很难了解书上那写代码是对应那些功能,以及书上没有给出的代码,我应该这么完善.那么,我就我自己的理解来说一下到目前为止(翻译成中间表示树以后,编译器的前端就算基本完成了),整个代码的布局是什么样. 首先

Cocos2d-x源码阅读 UI树2

Cocos2d-x的UI是按照树形结构组织的. 大家学过数据结构的话 就知道 什么是树了. 树只有一个 根节点,根节点没有父节点,其他节点都有父节点和子节点,而叶子节点没有子节点,叶子节点就是指没有子节点的节点. 在这里父和子 都是相对的. 我们知道树结构的遍历有3种方式,说是遍历 就是把每个节点找个遍的意思,前序遍历,中序遍历,后序遍历. 所谓的前,中,后指的是根节点,先遍历根节点就是前,后遍历就是最后查找根节点,中序遍历就是中间遍历. 所以 前序遍历的话 就是 0,-1, -3,3,2,-4

转载—— android 瀑布流的实现详解,附源码

介绍 参考自:https://github.com/dodola/android_waterfall,因为原来的代码封装不好,所以,我根据源码的思路,重新写了一遍,所以有了现在这个项目:https://github.com/youxilua/waterfall4android 原作者表示: 试过在1万张可以流畅的滑动,不出现内存溢出情况 设计思路 之前的作者的自定义view 只有主滑动一层,其他的设置要在相应的活动设置,个人觉得,重用起来比较麻烦,所以决定封装一层.现在定义一个默认的瀑布流只需5

Cocos2d-x源码阅读1 UI树(第一次系统而有成效的阅读源码的感悟)

之前我很少看源码,觉得枯燥又没有头绪.说实话现在看的也少,不过作为程序员要想成长,必须要突破自己的瓶颈吧. 也许我的天赋不在写代码这里,也许这是一个越走越难的路,也许这又是一个有金矿的浅坑,坚持下去就会挖到金矿. 然而没有那么多可以选择叻,试着去强大吧,即使自己不擅长,即使落后很多,即使,即使,即使,即使,.... http://cn.cocos2d-x.org/tutorial/show?id=2157 推荐大家去这里看这个视频,自己看源码可能没有头绪,跟着王老师的视频效果还是很好的. 这里是

[转载]ASP.NET Core 源码阅读笔记(3) ---Microsoft.AspNetCore.Hosting

有关Hosting的基础知识 Hosting是一个非常重要,但又很难翻译成中文的概念.翻译成:寄宿,大概能勉强地传达它的意思.我们知道,有一些病毒离开了活体之后就会死亡,我们 把那些活体称为病毒的宿主.把这种概念应用到托管程序上来,CLR不能单独存在,它必须依赖于某一个进程,我们把这种状况称之为:CLR必须寄宿于某一个进程中,而那个进程就是宿主. ASP.NET Core的一个大的改变就是就是将Web应用程序改成了自寄宿. 什么意思呢?我们知道,在之前的ASP.NET版本中,ASP.NET的We

[转载]ASP.NET Core 源码阅读笔记(2) ---Microsoft.Extensions.DependencyInjection生命周期管理

在上一篇文章中我们主要分析了ASP.NET Core默认依赖注入容器的存储和解析,这一篇文章主要补充一下上一篇文章忽略的一些细节:有关服务回收的问题,即服务的生命周期问题.有关源码可以去GitHub上找到. 这次的主角就是ServiceProvider一人,所有有关生命周期的源码几乎都集中在ServiceProvider.cs这个文件中. 我们知道服务的生命周期由三种,分别是: Transient Scoped Singleton 首先给出我的结论:这三种生命周期类别本质上没有区别,服务的生命周