七大查找算法——插值查找(三)

插值查找

定义: 插值查找,有序表的一种查找方式。插值查找是根据查找关键字与查找表中最大最小记录关键字比较后的查找方法。插值查找基于二分查找,将查找点的选择改进为自适应选择,提高查找效率。

简单来说,插值查找是基于二分查找不懂二分查找的点这里)改进的。区别在与,二分查找每次是选取正中间的值比较,会将查找范围分割为 0.5 :0.5 的两部分,实际就是乘以了1/2。而插值查找的选取方式不同,是(目标元素-最小元素)/(最大元素-最小元素)的比值的方式,不再是规定死的1/2。

比如,有一本英文字典,现在要你查找一个a字母开头的单次,你是会翻前面,还是直接往后翻。大部分情况我们会翻前面,因为我们知道a字母开头的单次靠近字典前面。所以我们正是基于这样的思想。

为什么是(目标元素-最小元素)/(最大元素-最小元素)的比值?
假设现在一共有1~100的数据,目标元素为3,那么(3-1)/ (100-1)= 2/99。得出的这个值是不是正好靠近最小元素1,而目标元素的确就是离1更近。实际上通过这个比值就很好反应了离最小元素(或最大元素)距离的思想。

具体实现步骤

1.初始化left,right。left = 0,right = length-1
2.当left<=right时,循环查找,否则跳出循环,并返回-1 。
3.选取比较值mid = left + (target - data[left]) / (data[right] - data[left]) * (right - left);
4.如果目标元素等于下标为mid的值,返回mid。
5.不等于,如果目标元素大于下标为mid的值,left = mid+1;如果目标元素小于下标为mid的值,right = mid -1 。然后跳回第3步。

复杂度分析

查找成功或者失败的时间复杂度均为O( log ⁡ 2 \log_2 log2( log ⁡ 2 N \log_2N log2N))。

适用场景:有序数列,对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比二分查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。

如果data = [1,2,3,10,20] ,target = 10,那么mid = 0+(10-1)/(20-1)*(4-0) = 1,反而还离目标元素更远了一点,所以数据均匀很重要。

完整代码

基于Java,jdk1.8实现。除了mid = left + (target - data[left]) / (data[right] - data[left]) * (right - left);其他与二分查找一样。不懂二分查找点这里

//插值查找
//基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
//          注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。
//        反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
//          复杂度分析:查找成功或者失败的时间复杂度均为O(log2(log2n))。
public class InterpolationSearch {
    public int interpolationSearch(int[] data, int target) {
        int left, right, mid;
        left = 0;
        right = data.length - 1;
        while (left <= right) {
            //            二分查找中,mid=left+(right-left)/2,拆开后是(left+right)/2。
            //            相当于每次加right-left之间一半的距离。
            //            现在是用(target-data[left])/(data[right]-data[left]),
            //            每次不是死板地加1/2距离,
            //            而是通过target与data[left]的差值去比上data[right]-data[left]。
            //            如data=[1,2,3,4,5],target = 2,那么第一次mid = 0+(2-1)/
            //            (5-1)*(4-0) = 1,直接定位到。
            //            但如果data = [1,2,3,10,20] ,target = 10,那么mid = 0+
            //            (10-1)/(20-1)*(4-0) = 1,反而还更远了一点,所以数据均匀很重要
            mid = left + (target - data[left]) / (data[right] - data[left]) *
                    (right - left);
            if (data[mid] == target) {
                return mid;
            } else if (data[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值