众所周知,java1.7的时候hashMap结构还是【数组+链表】,而在1.8版本结构变为了【数组+链表/红黑树】,当链表长度达到8时,自动转换为红黑树结构。
那么为什么java1.8要对hashMap的数据结构中加入树呢?
答案:提高查找效率。此前hashMap中的数据采取【数组+链表】的存储结构,桶数组会将通过hash算法将key值计算得来的相同哈希值数据存储在对应的链表中,而随着链表的数据增多,在检索结点方面,性能会下降,因为链表虽然插入删除的时间复杂度是O(1),但它遍历的时间复杂度达到了O(n)。为了使得时间复杂度更优,因此引入了树形结构,因为树查找快啊!
为什么那么多已经存在的树形结构,比如二叉查找树、平衡二叉树,为啥还需要红黑树?
首先,我们已经明确了,因为查找效率问题,hashMap的结构中需要树来提升性能。
而为什么不直接引入二叉查找树,或者二叉平衡树,这是因为他们有某种方面的致命缺陷。
1、为了提高查找性能,引入二叉查找树
二叉查找树的特点:左结点 < 根结点 < 右结点
基于这个特点,查找某个节点的时候,可以采取类似于 二分查找的思想,快速找到某个节点,因此时间复杂度为O(log n)
但有一种极端情况需要考虑到:
这种情况也是满足 二叉查找树 的条件,然而,此时的二叉查找树已经近似退化为一条链表,这样的二叉查找树的查找时间复杂度顿时变成了 O(n)
为了解决二叉查找树的极端情况下重新转化为
1、具有二叉查找树的全部特性。
2、每个节点的左子树和右子树的高度差至多等于1。链表问题,引入平衡二叉树
针对二叉查找树的缺点,我们考虑到可以引入二叉平衡树,因为它具有:左右子树高度相差最多不超过1的特点。
这种情况虽然能解决上述问题,但又会引出其他的问题。因为平衡二叉树要求 每个节点的左子树和右子树的高度差至多等于1 ,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过 左旋 和 右旋 来进行调整,使之再次成为一颗符合要求的平衡树。真是成也萧何败也萧何呀!
那么,有没有一种树形结构,既能在二叉查找树上优化左右子树高度差,又能在二叉平衡树上优化其严苛的特性呢?
红黑树,一种非严格意义上平衡的二叉查找树,完美解决问题。
红黑树,就是为了解决引入上述两个树带来的问题而产生的,用以在结点查找和树形旋转的性能优化中,寻求折中方案。
1、具有二叉查找树的特点。
2、根节点是黑色的;
3、每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存数据。
4、任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的。
5、每个节点,从该节点到达其可达的叶子节点是所有路径,都包含相同数目的黑色节点。
正是由于红黑树的这种特点,使得它能够在最坏情况下,也能在 O(logn) 的时间复杂度查找到某个节点。至于为什么就能够保证时间复杂度为 O(logn)。
总结:
二叉查找树是为了解决链表查找的性能问题,平衡二叉树是为了解决二叉查找树退化为链表的情况,而红黑树是为了解决平衡树在插入、删除等操作需要频繁调整的情况。