二分查找
也叫做折半查找,属于有序查找算法。
前提条件:数组数据必须有序,从小到大,或者从大到小都是可以的。
如果是无序的,也可以先进行排序。
但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。
基本思想:用给定值先与中间结点比较。
比较完之后有三种情况:
- 相等 :说明找到了
- 要查找的数据比中间节点小 : 说明要查找的数字在中间节点左边
- 要查找的数据比中间节点大: 说明要查找的数字在中间节点右边
public static void main(String[] args) {
//二分查找/折半查找
//核心:
//每次排除一半的查找范围
//需求:定义一个方法利用二分查找,查询某个元素在数组中的索引
//数据如下:{7, 23, 79, 81, 103, 127, 131, 147}
int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};
System.out.println(binarySearch(arr, 150));
}
public static int binarySearch(int[] arr, int number){
//1.定义两个变量记录要查找的范围
int min = 0;
int max = arr.length - 1;
//2.利用循环不断的去找要查找的数据
while(true){
if(min > max){
return -1;
}
//3.找到min和max的中间位置
int mid = (min + max) / 2;
//4.拿着mid指向的元素跟要查找的元素进行比较
if(arr[mid] > number){
//4.1 number在mid的左边
//min不变,max = mid - 1;
max = mid - 1;
}else if(arr[mid] < number){
//4.2 number在mid的右边
//max不变,min = mid + 1;
min = mid + 1;
}else{
//4.3 number跟mid指向的元素一样
//找到了
return mid;
}
}
}
插值查找
插值:是在二分查找的基础上,让中间的mid点,尽可能靠近想要查找的元素。
修改mid公式即可!
private static int binarySearch2(int[] arr, int num) {
int min = 0;
int max = arr.length - 1;
while (true) {
if (min > max) {
return -1;
}
int mid = min + (num - arr[min]) / (arr[max] - arr[min]) * (max - min);//这里改公式
if (num > arr[mid]) {
min = mid + 1;
} else if (num < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
}
细节:
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。
反之,数组中如果分布非常不均匀,那可以选折半二分
分块查找
当数据表中的数据元素很多时,可以采用分块查找。
汲取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找
分块查找适用于数据较多,但是数据不会发生变化的情况,如果需要一边添加一边查找,建议使用哈希查找
分块查找的过程:
- 需要把数据分成N多小块,块与块之间不能有数据重复的交集。
- 给每一块创建对象单独存储到数组当中
- 查找数据的时候,先在数组查,当前数据属于哪一块
- 再到这一块中顺序查找
public class BlockSearchDemo {
public static void main(String[] args) {
/*
分块查找
核心思想:
块内无序,块间有序
实现步骤:
1.创建数组blockArr存放每一个块对象的信息
2.先查找blockArr确定要查找的数据属于哪一块
3.再单独遍历这一块数据即可
*/
int[] arr = {16, 5, 9, 12,21, 18,
32, 23, 37, 26, 45, 34,
50, 48, 61, 52, 73, 66};
//创建三个块的对象
Block b1 = new Block(21,0,5);
Block b2 = new Block(45,6,11);
Block b3 = new Block(73,12,17);
//定义数组用来管理三个块的对象(索引表)
Block[] blockArr = {b1,b2,b3};
//定义一个变量用来记录要查找的元素
int number = 37;
//调用方法,传递索引表,数组,要查找的元素
int index = getIndex(blockArr,arr,number);
//打印一下
System.out.println(index);
}
//利用分块查找的原理,查询number的索引
private static int getIndex(Block[] blockArr, int[] arr, int number) {
//1.确定number是在那一块当中
int indexBlock = findIndexBlock(blockArr, number);
if(indexBlock == -1){
//表示number不在数组当中
return -1;
}
//2.获取这一块的起始索引和结束索引 --- 30
// Block b1 = new Block(21,0,5); ---- 0
// Block b2 = new Block(45,6,11); ---- 1
// Block b3 = new Block(73,12,17); ---- 2
int startIndex = blockArr[indexBlock].getStartIndex();
int endIndex = blockArr[indexBlock].getEndIndex();
//3.遍历
for (int i = startIndex; i <= endIndex; i++) {
if(arr[i] == number){
return i;
}
}
return -1;
}
//定义一个方法,用来确定number在哪一块当中
public static int findIndexBlock(Block[] blockArr,int number){ //100
//从0索引开始遍历blockArr,如果number小于max,那么就表示number是在这一块当中的
for (int i = 0; i < blockArr.length; i++) {
if(number <= blockArr[i].getMax()){
return i;
}
}
return -1;
}
}
class Block{
private int max;//最大值
private int startIndex;//起始索引
private int endIndex;//结束索引
//省略 构造 属性 等 JavaBean
}