概述
因为之前有准备写Java.util中的clloection集合类,最常见的就是List和Map,List中的ArrayList和LInkedList都已经总结完了,准备总结HashMap,jdk8中的HashMap底层结构是数组+单向链表+红黑树,关于红黑树的相关知识曾了解过,但也忘得差不多了,所以准备过一遍红黑树相关的知识,再总结HashMap的源码。
- 此文章仅为个人学习笔记,有理解错误的地方望指正。
二分查找法
- 概念
基于有序数组,进行二分、折半搜索某一特定元素,从数组的中间元素开始搜索,如果中间元素恰好是要查找的元素,那么搜索结束,如果要查找的元素大于或小于中间元素,就从大于或小于中间元素的那一半进行查找,在那一半中也是从中间开始查找,以此类推,每次查找时范围都会缩小一半。 - 实现
/**
*
* @param numbers 目标数组
* @param number 目标元素
* @param startIndex 开始下标
* @param endindex 结束下标
* @return
*/
private static int findNumber(int[] numbers,int number,int startIndex,int endindex){
//判断是否大于最大值小于最小值
if(number<numbers[startIndex]||number>numbers[endindex]||startIndex>endindex){
return -1;
}
//取中间数
int currentIndex=(startIndex+endindex)/2;
if(number>numbers[currentIndex]){
//大于从右边继续递归查找
return findNumber(numbers,number,currentIndex+1,endindex);
}else if(number<numbers[currentIndex]){
//小于从左边递归查找
return findNumber(numbers,number,startIndex,currentIndex-1);
}else{
//找到了,返回数据
return currentIndex;
}
}
二叉查找树
- 特点
- 左节点的值一定都小于根节点的值
- 右节点的值一定都大于根节点的值
- 左右节点也都是二叉树结构
如图
如图所示,假设要找到数字13
- 首先找到10节点,10节点发现13>10
- 于是往10的右叉树查找 找到了节点15
- 对比后发现13<15,又往15的左叉树查找,找到了节点13
看到这里发现,二叉树的思想其实就是二分查找法的思想,但是二叉树也存在一定的缺陷,如图:
可以看到,根节点的左叉树和右叉树元素数量差距很大,几乎变成了一个瘸子,虽然还是保持了二叉树的特性,但是查询的效率就大打折扣了,只要根节点足够大,几乎就是线性查找了,所以就有了二叉平衡树的出现,红黑树就是二叉平衡树的一种表现方式。
下图对比:
平衡二叉树就是左右两边的节点高度差值不会超过1,并且左右子树也是二叉平衡树,红黑树算是不严格的一种平衡二叉树,没有要求高度差必须不超过1,而是从根路径到最长路径不要超过最短路径的两倍,维持的是相对平衡,所以为什么有了平衡二叉树后还要有红黑树,两种树的应用场景是不同的,平衡二叉树对高度差有严格的要求,在查询的时候速度相对会快一点,红黑树对于高度差没有那么严格,所以在插入和修改某个元素的时候,对树的变动会比较小,可能有些时候只需要变色不需要自旋。
红黑树
- 特点,除了符合二叉查找树的基本特性外,还有以下:
·节点是红色或者是黑色
·根节点是黑色
·每个叶子节点都是黑色的空节点
·每个红色的节点的两个子节点都是黑色,不能有两个连续的红色节点
·从任意一节点到其每个叶子的所有路径都包含相同的黑色节点 - 实现
红黑树的实现,实际上就是通过变色、左旋、右旋的操作,使当前的树符合上面所说的几个特点,保持树的平衡,最大的目的就是降低树的高度,因为树的查找性能取决于树的高度。所以树的高度越低搜索的效率越高。
-
变色:把黑色节点变成红色节点 或者把红色节点变成黑色节点
-
左旋转:逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。如图
-
右旋转:顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。如图:
-
红黑树结构如下图
-
插入元素的一些示例
假设在上面的红黑树中添加节点9,需要变色
插入节点9为红节,并有两个黑色的空节点,此时违反了第四、第五条特性,出现了两个连续的红色节点,且根节点到最后一个叶子节点的黑色节点数量不相同,于是将父节点和叔父节点变为黑色,变色后符合红黑树的特性。
需要旋转的情况:
把之前的树稍微做了一下变动,如果要在上面的树中插入一个4节点,那么会是这样
很显然这是不符合上面所说的红黑树的几个特性的,这个时候看到4节点的父节点是红色,但是叔父节点是黑色,围绕节点7进行右旋,7节点成为原来左儿子的右儿子,七节点的父节点即现在的4节点染黑色,祖父节点即2节点染红色。
染色完成后,再绕着7的祖父节点2节点进行左旋,最后得到符合红黑树特性的树
- 删除元素的示例,以上面的树为例
首先删除节点1
删除1节点后,4节点取代1节点的位置变为红色,7节点变为黑色,原4节点的左子节点变为原1节点的左节点的右节点。
此时再删除7节点,直接删除后会如下所示
此时的树是违背了特性5的,所以又要重新进行一系列的变色和自旋,先绕0节点左旋,再绕4节点右旋,让树重新符合红黑树的特性,最后得到下图的树。
总结
红黑树其实就是在数据的插入或是删除过程中,遵循一些特定的规则,对树进行着色或是自旋,在这个过程中使树始终满足红黑树的五大特性。
关于插入或删除时应遵循的一些规则,这篇文章里面会写的比较详细:https://blog.csdn.net/li1914309758/article/details/80997342