面试之Java8中HashMap为什么使用红黑树而不使用AVL树

2021年最近刷面试题也常有见到这个点的问题就记录一下吧红黑树—存在的必要?算法原理?红黑树&&平衡二叉树 对比思考
推送:https://blog.csdn.net/weixin_39900608/article/details/111214986

1.平衡二叉树

一:它必须是二叉查找树。
二:每个节点的左子树和右子树的高度差至多为1。

为了减小查找效率的不确定性,稳定其查找次数。在二叉查找树中对其做了高度限制,这样就可以将查找次数稳定在(O(logn))内。
在这里插入图片描述

但是其插入时就费劲了。没有二叉查找树方便。二叉查找树,就是从根节点开始往下找,找到自己的位置,连接上就好了。二叉平衡树,为了满足条件二,需要在插入加节点之后,可能需要进行平衡操作。如上图,若在平衡二叉树中插入一个点 4
在这里插入图片描述

插入后将不满足(每个节点的左子树和右子树的高度差至多为1)条件。于是乎需要调节平衡。详细步骤此处不做详解。自行学习吧。

AVL树的查找、插入、删除操作在平均和最坏的情况下都是O(logn)

2 . 红黑树

红黑树详细定义:

1)每个节点或者是黑色,或者是红色。
2)根节点是黑色。
3)每个叶子节点(NIL)是黑色。[注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!
4)如果一个节点是红色的,则它的子节点必须是黑色的。
5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

在这里插入图片描述
故:
想一下,若有一颗树,其全都是黑色节点。那么为了同时5个条件。只有一种可能那就是满平衡二叉树。也就是说只有黑色节点的红黑树其实就是平衡二叉树。

我们将红色点想成是填充节点。即每次插入都以红色插入,且将黑色想成主要节点。红色节点做填充。

红色节点的作用就是使得树的高度更加灵活,不至于像平衡二叉树那样每次插入都需要 做平衡操作(减少了需要平衡的概率)。

红色节点的子节点是黑色节点)条件就使得高度受到限制,极限情况就是一个红色一个黑色串联。最多的查找次数 不会超过2倍的 最少的查找数。

红黑树的相关定义

总之
红黑树其实就是 二叉查找树和平衡二叉树 两者优缺点的一种折中。
可以防止出现二叉查找树那种 极差的情况
可以减少插入时平衡的次数。
其综合效率是最优的。但是查找效率,应该是平衡二叉树最快。
在这里插入图片描述

在Jdk1.8版本后,Java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度。

那么很多人就有疑问为什么是使用红黑树而不是AVL树,大家都知道平衡二叉树的平均查找复杂度是O(log(n)),查找效率是非常高的,可为啥后续改成红黑树结构呢

最主要的一点是理解一下转为红黑树的重要性吧,以上便是从插入的性能角度去分析RedBack Tree和AVL的区别会比较可观。

面试官:为什么Java8后不使用AVL树而使用红黑树?

对于小数据:

insert:

RB tree&avl tree具有恒定的最大旋转次数,但RB树会更快,因为平均RB树使用较少的旋转。

查找:

AVL树更快,因为AVL树的深度较小。

删除:

RB树具有恒定的最大旋转次数,但AVL树可以将O(log N)次旋转视为最差。并且平均而言,RB树也具有较少的旋转次数,因此RB树更快。

对于大数据:

insert:

AVL树更快。因为您需要在插入之前查找特定节点。当您有更多数据时,查找特定节点的时间差异与O(log
N)成比例增长。但在最坏的情况下,AVL树和RB树仍然只需要恒定的旋转次数。因此,瓶颈将成为您查找该特定节点的时间。

查找:

AVL树更快。(与小数据情况相同)

删除:

AVL树平均速度更快,但在最坏的情况下,RB树更快。因为您还需要在删除之前查找非常深的节点以进行交换(类似于插入的原因)。平均而言,两棵树都有恒定的旋转次数。但RB树有一个恒定的旋转上限。

总结原因:

(1)AVL以及红黑树是高度平衡的树数据结构。它们非常相似,真正的区别在于在任何添加/删除操作时完成的旋转操作次数

(2)两种实现都缩放为a O(lg N),其中N是叶子的数量,但实际上AVL树在查找密集型任务上更快:利用更好的平衡,树遍历平均更短。另一方面,插入和删除方面,AVL树速度较慢:需要更高的旋转次数才能在修改时正确地重新平衡数据结构。
(3)在AVL树中,从根到任何叶子的最短路径和最长路径之间的差异最多为1。在红黑树中,差异可以是2倍。
(4)两个都给O(log n)查找,但平衡AVL树可能需要O(log n)旋转,而红黑树将需要最多两次旋转使其达到平衡(尽管可能需要检查O(log n)节点以确定旋转的位置)。旋转本身是O(1)操作,因为你只是移动指针。

对于为什么链表的长度超过8会转换成红黑树?

红黑树的插入、删除和遍历的最坏时间复杂度都是log(n), 因此,意外的情况或者恶意使用下导致hashCode()方法的返回值很差时,
性能的下降将会是"优雅"的,只要Key具有可比性。 但由于TreeNodes的大小是常规Nodes的两倍,所以只有桶中包含足够多
的元素以供使用时,我们才会使用树。那为什么这个数字是8呢

1.从概率学的分布规律解析

Because TreeNodes are about twice the size of regular nodes, we
use them only when bins contain enough nodes to warrant use
(see TREEIFY_THRESHOLD). And when they become too small (due to
removal or resizing) they are converted back to plain bins. In
usages with well-distributed user hashCodes, tree bins are
rarely used. Ideally, under random hashCodes, the frequency of
nodes in bins follows a Poisson distribution
(http://en.wikipedia.org/wiki/Poisson_distribution) with a
parameter of about 0.5 on average for the default resizing
threshold of 0.75, although with a large variance because of
resizing granularity. Ignoring variance, the expected
occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
factorial(k)). The first values are:

0: 0.60653066
1: 0.30326533
2: 0.07581633
3: 0.01263606
4: 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006
more: less than 1 in ten million

译文:

因为树节点的大小是普通节点的两倍,我们
仅当垃圾箱包含足够的节点时才使用它们(见TREEIFY_THRESHOLD)。当它们变得太小(由于移除或调整大小)它们被转换回普通的箱子。在使用分布良好的用户hashCodes,树桶是很少使用。在理想情况下,在随机hashCodes下,频率为桶中的节点遵循泊松分布
参数的默认大小平均约为0.5阈值为0.75,尽管有很大的差异,因为调整粒度。忽略方差,也就是期望列表大小k的出现次数是(exp(-0.5)

  • pow(0.5, k) /阶乘(k))。第一个值是:

可见在随机哈希代码下,桶中的节点频率遵循
泊松分布,文中给出了桶长度k的频率表。
由频率表可以看出,桶的长度超过8的概率非常非常小。所以作者应该是根据
概率统计而选择了8作为阀值。

猜你喜欢

转载自blog.csdn.net/weixin_42754971/article/details/113484090