1、红黑树
《算法导论》中对于红黑树的定义:
- 每个节点或是红的,或是黑的
- 根节点是黑的
- 每个叶节点是黑色(叶节点用Null来表示 Show Null Leaves) 【这里的叶子结点是Null的结点】
- 如果一个结点是红色,则它的两个儿子都是黑色(不能出现连续的红节点)
- 对每个结点,从该节点到其子孙结点的所有路径上包含相同数目的黑节点(黑节点平衡)
NULL结点
- 插入新节点:默认是红色
- 变色操作
- 例如:依次插入10、20、30
会进行 变色 + 左旋



红黑树新插入结点p:

p的父节点是黑色,则不用进行调整;
p的父节点是红色:
- 叔叔结点是空的(空节点默认是黑色),则 旋转(优先旋转祖孙三代) + 变色(G和P都变色)

- 叔叔结点 25 是红色,则 父节点 + 叔叔结点 变为黑色,祖父结点变为红色
====> 
- 叔叔结点 25 是黑色, 左旋 + 变色
===>
==>
左旋===>
变色===> 
插入结点4(调整祖孙三代)右旋==>
变色 ==> 
再插入结点6
===> 结点5先左旋,结点10再右旋 ===>
===> 结点10再右旋
2、HashMap
2.1 TreeNode - 红黑树的结点
prev 双向链表

2.2 put


2.3 红黑树的插入:balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x)
root:根节点;x:带插入的结点
新节点x 默认为红色;
xp表示x的父节点,xpp表示x的祖父结点,xppl表示xpp的左孩子结点,xppr表示xpp的右孩子结点;


3、JDK1.8 hashmap源码
3.1 put
首先,对key进行哈希(或运算和右移运算),计算key应该放到哪个数组的位置。


Node:

put关键方法:putVal
resize()方法:既包括对数组的初始化,又包括对数组的扩容。
当存在相同的key时,那么更新结点e时,会返回原来结点的oldValue。
使用尾插法:p.next = newNode(hash, key, value, null)
插入之后,必须进行判断 是否树化:treeifyBin(tab, hash)
if(binCount >= TREEIFY_THRESHOLD - 1)
当 binCount >= 7,也即是链表长度 >= 8 会树化


链表转化为红黑树:
双向链表:

判断数组的大小若小于64,则不会树化,只会resize() --> 扩容
![]()
树化:将原来链表的Node 重新构造为 TreeNode,并且生成双向链表。hd 表示生成的双向链表的第一个元素。

树化方法:hd.treeify(tab)
遍历双向链表,其第一个结点作为树的root结点,下一个结点插入到红黑树中。随着红黑色结点的增加,root结点会发生改变。

树化插入结点时的比较规则:
- hashcode 先比较key的哈希值 :(ph = p.hash) h进行比较
- compareto()
- getClass().getName compareTo
- System.identityHashCode(a)



moveRootToFront:将红黑树的root结点移动到hashmap的tab[i]链表头,替换掉原来的链表;同时将双向链表的root结点移动至链表的第一个结点位置。

469

被折叠的 条评论
为什么被折叠?



