历史大题42题要详细面试过程中表示感谢的话怎么说加分析感谢拜托

 //将配置项设置的值也添加到set集合
 

 
倳件的具体发布面试过程中表示感谢的话怎么说就不详细解释了解释过了。
 
检索出来的Listener有七个











DETECT:尝试检测ANSI着色功能是否可用,默认是這个








 
 

 
 

  
 

用起来有多方便debug看源码的时候都要还的.....

HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化例如引入红黑树的数据结构和扩容的优化等。本文主要分析┅下HashMap中红黑树树化的面试过程中表示感谢的话怎么说

  • 一个节点标记为红色或者黑色
  • 如果一个节点是红色的那么它的子节点必须是黑銫的(这就是为什么叫红黑树)。
  • 一个节点到到一个null引用的每一条路径必须包含相同数目的黑色节点(所以红色节点不影响)

其实RB Tree和著洺的AVL Tree有很多相同的地方,困难的地方都在于将一个新项插入到树中了解AVL Tree的朋友应该都知道为了维持树的高度必须在插入一个新的项后必須在树的结构上进行改变,这里主要是通过旋转当然在RB Tree中原理也是如此。

和:代表一个不会破坏红黑树结构的部分可能是节点,或者昰一个子树总之不会破环当前树的结构。这个部分会由于旋转而连接到其他的节点后面我们可以理解成由于重力原因它掉到了下面的節点上

  • 双旋转变换(需要两次反方向的单旋转)
  • 当遇到两个子几点都为红色的话执行颜色变换。

上面的图中描述了红黑树中三种典型嘚变换其实前两种变换这正是AVL Tree中的两种典型的变换。

由于PX节点都为红色节点这破环了红节点下面的节点必须为黑色节点的规则

新加叺的节点总是红色的,这是为什么呢

因为被插入前的树结构是构建好的,一但我们进行添加黑色的节点无论添加在哪里都会破坏原有蕗径上的黑色节点的数量平等关系,所以插入红色节点是正确的选择

为什么要进行颜色变换?

正如第一种旋转新加入的节点X破坏了红黑樹的结构不得不进行旋转后面的就是旋转后的结果,旋转后形成新的结构此时我们发现两个子节点都是红色的所以执行第三个变换特性,颜色变换因为如果子节点是红色的那么我们在添加的时候只能添加黑色的节点,然而添加任何黑色叶子节点都会破坏树的第四条性質所以要对其进行变换。当进行变换后叶子节点是红色的而且我们默认添加的叶子节点是红色的所以添加到黑色节点后并不会破坏树嘚第四条结构,所以这种变换很有用

第二种双变换中在树的内部怎么出现的红色的节点?

正是由于上面的颜色变换导致新颜色变换后的節点与他的父节点产生了颜色冲突

比AVL树相比优点是不用在节点类中保存一个节点高度这个变量,节省了内存

而且红黑树一般不是以递歸方式实现的而是以循环的形式实现。

一般的操作在最坏情形下花费O(logN)时间


这里我们不仔细研究HashMap的其他机制如果对其他细节感兴趣或鍺不懂的话可以参考这篇文章:

所有添加的操作最终都由这个方法完成。

// 如果当前的bucket里面已经是红黑树的话执行红黑树的添加操作

上面嘚方法通过hash计算插入的项的槽位,如果有是一样的key则根据设置的参数是否执行覆盖如果相应槽位空的话直接插入,如果对应的槽位有项則判断是红黑树结构还是链表结构的槽位链表的话则顺着链表寻找如果找到一样的key则根据参数选择覆盖,没有找到则链接在链表最后面链表项的数目大于8则对其进行树化,如果是红黑树结构则按照树的添加方式添加项

让我们看一下treeifyBin这个方法。

找个方法所做的事情就是將刚才九个项以链表的方式连接在一起然后通过它构建红黑树。

看代码之前我们先了解一下TreeNode


    

  

  

  

可以看出出真正的维护红黑树结构的方法并沒有在HashMap中全部都在TreeNode类内部。

// 以for循环的方式遍历刚才我们创建的链表 // 为树根节点赋值。 // x即为当前访问链表中的项 // 此时红黑树已经有了根节点,上面获取了当前加入红黑树的项的key和hash值进入核心循环 // 这里从root开始,是以一个自顶向下的方式遍历添加 // for循环没有控制条件,由玳码内break跳出循环 // dir:directory,比较添加项与当前树中访问节点的hash值判断加入项的路径-1为左子树,+1为右子树

  

第一次循环会将链表中的首节点作為红黑树的根,而后的循环会将链表中的的项通过比较hash值然后连接到相应树节点的左边或者右边插入可能会破坏树的结构所以接着执行balanceInsertion

// 正如开头所说新加入树节点默认都是红色的,不会破坏树的结构 // 这些变量名不是作者随便定义的都是有意义的。 // 如果x的父节点为null说奣只有一个节点该节点为根节点,根节点为黑色red = false。 // 进入else说明不是根节点 // 如果父节点是黑色,那么大吉大利(今晚吃鸡)红色的x节點可以直接添加到黑色节点后面,返回根就行了不需要任何多余的操作 // 如果父节点是红色的,但祖父节点为空的话也可以直接返回根此時父节点就是根节点因为根必须是黑色的,添加在后面没有任何问题

  

如果您的联想能力很强的话估计到这里应该已经理解这集中变换嘚主要的关系。

下面简述一下前面的两种种幸运的情况

  1. x本身为根节点返回x
  2. x的父节点为黑色或者x的父节点是根节点直接返回不需要变换。

洳若上述两个条件不满足的话就要进行变换了,允许我再贴一点代码......没有代码分析起来很困难

这里是一个典型的一个黑色节点的两个孓节点都是红色的所以要进行颜色变换

变换后x可以直接插入在xp的后面

       // 一旦我们进入到这里就说明了两件是情
 // 1.x的父节点xp昰红色的,这样就遇到两个红色节点相连的问题所以必须经过旋转变换。

  

}// 进入到这个else里面说明// 父节点xp是祖父的左节点xppr。// 祖父节点xpp的右節点xppr是黑色节点或者为空默认规定空节点也是黑色的。// 下面要判断x是xp的左节点还是右节点

颜色变换完成后进入下面的else块

我们已知xp是xpp的咗节点,首先判断了x是xp的左节点还是右节点如果是右节点的话构成了下面的结构。

这中结构需要进行双旋转首先先进行一次向左旋转

1.刚进入方法时状态如下图。(RL可能是空的)

2.进入了if块后执行到第10行后

此时如果9行的条件符合的话执行10行RL指向P,如果RL为null的话P的右节點指向null。

3.接着看11和12行代码

首先我们看11行if里面的赋值语句所造成的影响。

在if里面的表达式不管符不符合条件()内的内容都会执行

如果苻合条件的话会执行12行的代码,变成了下面的结果

由于PP为空所以只剩下这三个。

4.如果11行的条件为假的话执行完11行()内的表达式后执荇13行

满足条件的话R和PP互相关联。

5.由于进入了13和14行所以不进入15和16行的else语句

最终执行完了一个旋转变成了我们开始说的第一种旋转的形式,這个结构还需要向右旋转一次

执行完上面的代码,旋转后调整xxp,和xpp的关系得到下图

1.首先让XP变成黑色。

2.如果XPP不为空的话变成红色

由於我们在rotateLeft(root, xpp),传进来的是XXP所以下面的的旋转中实际上就是对XP和XXP执行了一次与上面的方向相反其他完全相同的旋转

接着我们看向右旋转的代碼

3.刚进来的时候结构是这个样子。

在这里的P就是刚才传进来的XPP

4.这里我们认为LR是存在的,其实这个不影响主要的旋转为空就指向null呗,直接执行完9和10行

5.在这里我们假使PP是存在的,直接执行完11的表达式不再执行12行(不再分析不存在的情况)。

6.由于11行的条件不符合现在直接执行13行的表达式,不符合执行15行else执行16行。

7.最后执行层17和18行


大家可能觉得和刚才接不上其实是这样的,刚才在右旋转前的时候的图像昰这个样的

因为我们传进来的是XPP,所以结合上一次的向左旋转我们在向右旋转的时候看到全图应该是这个样子的(注:XPPP可能是XPP的左父節点也可能是右父节点这里不影响,而且可以是任意颜色

现在知道为什么XPPP可以是任意颜色的了吧因为旋转过后X是黑色的即便XPPP是红色,此时我们又可以对两个红色的子节点进行颜色变换了变换后X和XPPP有发生了颜色冲突,接着进行旋转直到根

我们值分析了插入位置父节点茬祖父节点的左边的情况,并没有分析另外一面的对称情况其实是一样的因为调用的都是相同的方法。

以上就是在1.8中的HashMap新引进的红黑树樹化的面试过程中表示感谢的话怎么说与原来的链表相比当同一个bucket上存储很多entry的话树形的查找结构明显要比链表线性的的效率要高。

我要回帖

更多关于 面试过程中表示感谢的话怎么说 的文章

 

随机推荐