Java算法总结(2)_插值查寻法

1、插值原理

  在上一章中介绍了二分法查找,可以知道二分法是通过对半选择查找位置mid,然而在现实生活中,对于排序均匀有规律的列表,比如字典、电话簿,在查找姓氏“张”的时候,我们都会下意识的从末尾几页开始查找,而不是像二分法那样从中间入手,同理,插值查寻法也是将查找位置mid的对半选择改进为自适应选择(相似三角形定理),从而提高了查找效率;
在这里插入图片描述
其中:

  • mid:查找位置;
  • low:列表的下界值;
  • high:列表的上界值;
  • searchVal:要查找的值;
2、 插值法特点

1、 有序列表;
2、 列表中的数据分布均匀,逼近线性;
3、 时间复杂度O(logn);

3、图解

在这里插入图片描述

3.1、图分析

1、 左边图中数据呈线性递增趋势,右边图中数据呈非线性递增,先凹后凸,有一个拐点;
2、 左图无论查寻值searchVal为多少,只要在[a[low] ,a[high]]之间,就能一次性找到;
3、 在右图中,只要查寻值searchVal产成的两个交点point1和point2未重合,那么就需要通过向左或向右多次循环逼近重合,结束查找;

4、实例代码测试
package xw.zx.algorithm.search;

import java.util.Arrays;
import java.util.Date;
import java.util.Random;

public class InterpolationSearch {
	private static final Random r=new Random();
	private static final int ARRAY_SIZE=1000;
	private static  Integer[] list=new Integer[ARRAY_SIZE];
	public static void main(String[] args) {
		InterpolationSearch interpolationSearch=new InterpolationSearch();
	 	//生成数组中有序的元素;
		for(int i=0;i<ARRAY_SIZE;i++) {
			list[i]=i+1;//interpolationSearch.generatorRand(1,ARRAY_SIZE*10);
			
		}
		Arrays.sort(list);
		//输出数组长度;
		System.out.println("有序数组长度:"+ARRAY_SIZE);		
		//随机生成需要查询的数值
		int searchVal=list[r.nextInt(ARRAY_SIZE)];
		int count;
		long startTime,endTime;
		System.out.println("--------------插值查寻法--------------------");
		//开始时间
		
		startTime=new Date().getTime();
		//调用插值法查找;
		count=interpolationSearch.insertValueSearch(list,searchVal);
		//结束时间
		endTime=new Date().getTime();
		System.out.println("算法运行时间:"+(endTime-startTime)+"ms");
		//输出查询值和查询次数
		System.out.println("searchVal:"+searchVal+", count:"+count);
		
		System.out.println("--------------二分查寻法--------------------");
		//开始时间
		startTime=new Date().getTime();
		//调用二分法查找;
		count=interpolationSearch.binarySearch(list,searchVal);
		//结束时间
		endTime=new Date().getTime();
		System.out.println("算法运行时间:"+(endTime-startTime)+"ms");
		//输出查询值和查询次数
		System.out.println("searchVal:"+searchVal+", count:"+count);

	}	
	/**
	 * 随机数生产
	 * @param start
	 * @param end
	 * @return
	 */
	public int generatorRand(int start,int end) {
		int randVal;
		randVal=r.nextInt(end-start)+start;
		return randVal;
	}
	
	/**
	 * 插值查寻法
	 * 算法的运行时间为O(logn);
	 * @param arr
	 * @param searchVal
	 * @return
	 */
	public int insertValueSearch(Integer[] arr,int searchVal) {
		//初始化查询次数、下界low、上界high
		int count=0,low =0,high=arr.length-1;
		int mid,guess,temVal=0;
		while(low<=high) {
			/**
			 *1、自适应选择 
			 *2、乘上1.0转为double类型,在进行除运算,可避免整数除以整数后,只保留整数的情况
			 *3、 mid=low+(searchVal-arr[low])*(high-low)/(arr[high]-arr[low]);这种写法(searchVal-arr[low])*(high-low)计算可能会溢出Integer
			 */
			 mid=low+(int)((searchVal-arr[low])*1.0/(arr[high]-arr[low])*(high-low));			
			 guess=arr[mid];			
			 System.out.println("guess:"+guess);
			 count++;
			 //找到值
			 if(guess==searchVal) {
				 System.out.println("下标:"+mid );
				 return count; 
			 }
			 //定界	 
			 if(guess>searchVal) {
				 high=mid-1;
			 }else {
				 low=mid+1;
			 }
			
		}
		return -1;
	}
	
	/**
	 * 二分查寻法
	 * 算法的运行时间为O(logn);
	 * @param arr
	 * @param searchVal
	 * @return
	 */
	public int binarySearch(Integer[] arr,int searchVal) {
		//初始化查询次数、下界low、上界high
		int count=0,low =0,high=arr.length-1;
		int mid,guess;
		while(low<=high) {
			//对半查找
			//mid=low+(int)(0.5*(high-low));
			 mid=(low+high)/2;		
			 guess=arr[mid];
			 System.out.println("guess:"+guess);
			 count++;
			 //找到值
			 if(guess==searchVal) {
				 System.out.println("下标:"+mid );
				 return count; 
			 }
			 //定界	 
			 if(guess>searchVal) {
				 high=mid-1;
			 }else {
				 low=mid+1;
			 }
			
		}
		return -1;
	}

}
4.1、线性分析

  对于线性递增数据,插值查寻始终能够在一次搜索中,找到查找值searchVal,而二分法则需要循环对半逼近searchVal,所以在这种情况下,插值法效率更高;
在这里插入图片描述

4.2非线性分析

  在非线性递增的情况下,插值法不再能够每次都一次性找到searchVal,但是跟二分法比较,当被查寻的列表数据量越大时,插值法的查寻次数也会比二分法少;
在这里插入图片描述

5、总结

1、插值法采用自适应选择的方式确定查找位置mid;
2、列表中的数据越接近线性递增或递减时,插值法的查找效率越高;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值