插值查找易懂解析
注意:插值查找和二分查找都需要数组是有序的才可以进行查找
假设我有一组有序的线性表{1,2,3,4,...,20},我们来利用二分查找来找1,看看它会经过几次能找到我们的1代码如下:
/** * * @param arr 要查找的数组 * @param left 左边下标 * @param right 右边下标 * @param findVal 要查找的数 */ public static int binarySearch(int[] arr,int left,int right,int findVal){ System.out.println("调用的次数!!"); //定义中间变量索引并初始化 int mid = (left + right) /2; //获取中间变量索引对应的下标 int midVal = arr[mid]; //如果找不到,我们结束递归的条件是left > right if (left > right){ return -1; } //进行查找 if (findVal >midVal){ //表示要查找的值在右边,我们递归处理 //说明: 右边查找我们需要改变左边的下标也就是从 mid+1处开始去递归处理结果 return binarySearch(arr,mid +1,right,findVal); }else if (findVal < midVal) { //表示要查找的值在左边,我们递归处理 //说明: 左边查找我们需要改变右边的下标也就是从 left - mid-1之间去递归处理结果 return binarySearch(arr,left,mid -1,findVal); }else { return mid; } }
- 测试代码
public static void main(String[] args) { int [] arr = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; int result = binarySearch(arr, 0, arr.length - 1, 1); System.out.println("result ="+result);
-
来看测试结果:
插值查找算法介绍
其实插值查找算法的过程跟二分查找的类似,二者唯一的区别是插值查找每次都能从自适应的mid(中间值或者是中间索引或者是下标)处开始找,还记的我们在二分查找算法中求解mid的过程?
- 二分查找mid的计算公式
mid = (left+right)/2 = left + 1/2(right -left)
其中left表示左边下标,right表示右边下标,我们需要对该上述的计算过程进行改造如下:
mid = left +(right -left)*(findVal -arr[left])/
(arr[right] - arr[left])
其中 findVal表示我们要查找的数;arr[left]表示左边索引对应的数;arr[right]表示右边索引对应的数,这就是插值查找算法mind索引的计算公式,这里我们简单的自测下它到底有多快,假设我有从1-100的数组,我们来测试一下:
- 需求1: 从arr[1,2,3,...,100]中查找1,代入上述的公式来计算下
int mid = 0+(99-0)* (1- arr[0]) / (arr[99] - arr[0]) =
0 +99 *(1- 1) /(100 - 1) = 0
计算的结果为0,也就是arr[0] = 1.发现我们只需要一次就可以了找到1这个数,再来测试下100这个过程,同样计算如下:
int mid = 0 +(99 - 0) * (100 - arr[0]) / (arr[99] - arr[0]) =
0+ 99 * (100 - 1) /(100 -1) = 0 + 99 = 99
计算结果可以看出为99,同样也需要1次就可以找到,这里也就是 arr[99] = 100,看完了过程我们用代码的方式来实现下从1-100的数组中去找的这个过程
代码实现
//插值查找方法 //说明:插值查找算法也是有序的 /** * * @param arr 待查找的数组 * @param left 左边下标 * @param right 右边下标 * @param findVal 要查找的值 * @return */ public static int insertValSearch(int[] arr,int left,int right,int findVal){ System.out.println("查找的次数--"); //判断 if (left > right|| findVal <arr[0] || findVal> arr[arr.length -1]){ return -1; } //首先我们计算出中间索引mid int mid = left +(right - left) * (findVal - arr[left]) /(arr[right] -arr[left]); //初始化mid索引所对应的值 int midVal = arr[mid]; if (findVal > midVal){ //表示从mid的右边开始递归找 return insertValSearch(arr,mid+1,right,findVal); }else if (findVal < midVal){ return insertValSearch(arr,left,mid -1,findVal); }else { //表示找到 return mid; } }
- 来看测试代码
public class InsertValueSearch { public static void main(String[] args) { int [] arr = new int[100]; for (int i = 0; i < 100 ; i++) { arr[i] = i +1; } //System.out.println(Arrays.toString(arr)); int indexResult = insertValSearch(arr, 0, arr.length-1, 1); System.out.println("result = "+indexResult); }
- 测试结果如下
我们发现通过一次就能找到1了,我们来测试下二分查找需要几次,测试结果如下: