1.0、二分查找算法:
前提:是“有序”的元素列表
原理:每次都与“中间的数”比较看是大了还是小了,下一次的猜测直接排除一半的可能,从另外一半继续查找,重复这个过程,直到查找成功或者查找集为空
适用场景:本身就是有序的存储,或者维持有序的代价不高,就可以用这个,很快(太简单的直接顺序查找即可)
时间复杂度:logN
1.1、插值查找算法 -》 二分查找算法的特制版,但是除了二分查找算法的限制外,还需要分布均匀:
前提:是“有序”的“知道界限”的元素列表,元素大小分布需要比较均匀
原理:和二分查找原理差不多,只是分割点的选择不同(不再是中间,而是根据公式选择一个位置,会找到更靠近查找值的位置,所以前提是分布均匀),中间分割点是根据一个公式计算得出,公式可以去看"大话数据结构",
适用场景:本身就是有序的存储,或者维持有序的代价不高,元素大小分布均匀,就可以用这个代替二分查找算法
时间复杂度:logN , 虽然时间复杂度和二分查找算法差不多,但在元素比较多且分布比较均匀的时候,实际效率比二分查找好很多
1.2、斐波那契查找算法 -》:二分查找算法的特制,需要是斐波那契数列
前提:元素的排列是斐波那契数列,斐波那契数列指的是这样一个数列 - 从第3项开始,每一项都等于前两项之和。
原理:和二分查找原理类似,只是分割点的选择不同,详细的可以去看"大话数据结构"
适用场景:斐波那契数列
时间复杂度:logN , 虽然时间复杂度和二分查找算法差不多,但因为计算公式比二分查找、插值查找简单,海量数据下效率更高
2、对于海量数据,上面的算法查找还需要几次比较,维持有序的代价也比较高,用索引可以更快(需要查询事前维持)
稠密索引:索引项与数据集的记录个数相同,一一对应,普通的索引方式;可以通过二分查找、插值查找
分块索引:块间有序,块内无序(块内也可以有序,但那样代价高昂);减少了索引个数;索引结构有- 块内最大值、块内元素个数、块首数据元素的指针;可以通过二分查找、插值查找先找到块,再通过顺序查找找到记录
倒排索引:单词表 - 存储具有相同关键字的对应的所有记录号,根据属性值找到所有记录。
3、二叉排序树 - 上面算法只是应对查找用的,而二叉排序树增删查都很有效率;特别是平衡二叉树,高度更低,查找效率更高;但维持绝对的平衡代价太高,一般相对平衡即可(红黑树):
二叉排序树:左子树为空或者所有左节点值小于根节点的值,右子树为空或者所有右节点值大于根节点的值,其左右子树也是二叉树
高度平衡二叉树:每一个节点的左子树和右子树的高度差最多==1;每个节点的左子树和右子树也都是高度平衡二叉树
红黑树:一种含有红黑结点并能自平衡的二叉排序树(黑色完美平衡),这个用得多
说起红黑树,不得不说redis的跳表(红黑树的替代品,但实现更加简单,范围查询方便;原理就是多级的链表 https://www.cnblogs.com/cxy2020/p/13799047.html)
4.0、B树 -》 平衡多叉树,二叉排序树是内存中用的,如果是海量数据,就要考虑到磁盘交互问题,这是重点,一次读取多个会比较好(每次硬盘读取的都是一个或多个页面,每个页的长度是211到214的字节)
但是B树不适合遍历、范围查找,B+树出现了
4.1、B+树 -》 B+树是B树的一个升级版,B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
规则:
1)B+ 树非叶子节点上是不存储关键字的,仅存储分块的最大键值以及指针,而 B 树节点中不仅存储分块的指针,也会存储关键字
2)B+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的
一般根节点都是常驻内存的,开始不需要到磁盘中读取数据,直接从内存中读取即可。
5、散列hash表 -》 存储位置与关键字之间建立一个关系,使关键字对应到相应的存储位置上
地址冲突了,一般采用链地址法(有最佳的查找性能)
重要因素:
1)散列函数是否均匀
2)装填因子要小,大于0.75需要扩容
大多数的时候散列表查找性能最佳,但是不适合的场景是"关键字重复度高"、“范围查找”,它只适合一对一查找
按照性能来看,散列hash最好(直接寻址),索引查找,磁盘交互用B+树 / 内存增删查用红黑树,有序序列的查找用二分/插值/斐波那契,实在不行直接顺序查找;一般都是组合使用(比如kafka根据offset找数据就是通过有序offset+segment+二分查找+分块索引+二分查找+稠密索引+顺序查找)
二分查找的代码实现:
private static int binarySearch(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] > key) {
high = mid - 1;
} else {
low = mid + 1;
}
}
// 查找失败,返回 -1
return -1;
}