插值查找设计原理剖析

系列文章目录

 

超基础——数据结构与算法专栏(如果你也想学习数据结构设计原理深入了解数据结构可以收藏该专栏我们一起进步!!!

https://blog.csdn.net/m0_64231944/category_12014814.html?spm=1001.2014.3001.5482https://blog.csdn.net/m0_64231944/category_12014814.html?spm=1001.2014.3001.5482

文章目录

前言

本文我们学习的是二分法查找的优化算法——插值查找算法

正文:

回顾二分法查找算法:

二分法查找代码实现:

二分查找优化之插值查找;

关键的地方:

插值查找代码实现:

二者区别:

总结


        我是一名大二在读学生。 刚刚自学完java基础。

        目前呢,我也是在自学数据结构与算法——我会将数据结构中难理解的问题,总结在我的博客上,如果你也是这样,可以收藏该专栏关注我我们一起进步!!!


前言

本文我们学习的是二分法查找的优化算法——插值查找算法


正文

回顾二分法查找算法:

        二分查找(Binary Search)也叫作(拆半查找)二分查找有两个要求:

1,数列有序。

2,数列使用顺序存储结构(比如数组);

        二分查找的实现原理非常简单,首先要有一个有序的列表。但是如果没有,则该怎么办?可以使用排序算法进行排序。
        我们以升序数列为例,比较一个元素与数列中的中间位置的元素的大小,如果比中间位置的元素大,则继续在后半部分的数列中进行二分查找;如果比中间位置的元素小,则在数列的前半部分进行比较;如果相等,则找到了元素的位置。每次比较的数列长度都差不多会是之前数列的一半,直到找到相等元素的位置或者最终没有找到元素,程序退出并告诉程序员未找见。

代码实现:

public static boolean binarysearch(int[] arr, int num) {
    if (arr == null || arr.length == 0) {
        return false;}//先处理边界条件
    int L = 0;
    int R = arr.length - 1;
    while (L <= R) {
        int mid = (R + L) / 2;
        if (num == arr[mid]) {
            return true;
        } else if (num < arr[mid]) {
            R = mid - 1;
        } else if (num > arr[mid]) {
            L = mid + 1;
        } }
    return false;}

 值得注意的是:上方代码中  mid  的求法。

int mid = (R + L) / 2;

下面我们介绍优化后的插值查找; 

二分查找优化之插值查找;

首先我们先思考一个问题

        在二分查找算法中,为什么是二分查找,而不是三分之一、四分之一查找。
        发散一下思维,在查字典的时候,如果要查以a开头的单词,则你会怎么翻字典?肯定是从最前面开始翻;如果要查以 z 开头的单词,则应该会从最后开始翻。显而易见,你不会采用二分查找的方式去查这个单词在哪,因为这样你会很累。同样,假设数据的范围是 1~10000,让你找 10,你会怎么样?简单来说,我觉得干脆用顺序查找好了,因为数列是升序的,没必要用二分查找,用顺序查找比二分查找的比较次数少。
        所以经过这样的考虑,我们可以优化一下二分查找,并不一定要从正中间开始分,而是尽量找到一个更接近我们要找的那个数字的地方,这样能够减少很多查找次数
        之前我们都是根据长度去找到这个中间位置,现在是根据 key 所在的序列范围区间去找到这个位置。比如数列是 1~10,待查 key 是 3,我们可能会将大概前面三分之一的地方作为这个划分点。
        不过还是有人给出了更精准的计算方式,即要查找的位置 P,这是有点复杂,但是仔细看一下,这种计算方式其实就是为了找 key 所在的相对位置,让 key 的值更接近划分的位置,从而减少比较次数。

P=low+(key-arr[left])/(a[right]-a[left])×(right-left);

        前面介绍的二分查找,其复杂度为O(logn),在数据量较多的情况下比顺序查找效率会高很多:当然,二分查找要求数据是有要求的,他要求必须是有序的,这也规定了其适用范围。此时我们在二分查找的基础上进一步的约束数据,要求数据是有序且数值分布均匀的,可以获得更加高效的“插值查找”算法

        在二分查找中1/2代表区间长度每次缩减一半,也就是控制区间缩减幅度的因子二分查找中并没有考虑数据中数值的情况仅仅使用了数值是有序的这一信息

现在,来看下述的数值有序且分布均匀数据:

data = [1, 2, 3, 5, 6, 7, 10, 13, 14, 15, 16, 17, 19, 22, 23, 25, 27]、

关键的地方到了:

        在上面的数组中如果搜索的目标是2,那么区间长度每次都缩减为一半合理吗?显然是不合理的,因为2这个值比较小,明显比较偏向于数据的起始位置;如果搜索的目标是25,区间减半的方式同样不合理。

所以,可以改变二分查找的区间缩减策略,根据搜索的值来确定区间缩减幅度,使其不再是固定的1/2,这种想法就是“插值查找”,其中间位置计算方式如下:

如果alpha用于衡量搜索目标值距离左边界的远近,就可以使用下述公式进行表达,其中tar表示目标值,D表述有序数组:

此时,如果目标值tar和左边界值D[left]差的多,则中间位置mid更靠右;如果目标值tar和左边界值差的少,则中间位置mid更靠左。也就是说插值查找算法的中间位置mid不是真的在中间了,而是根据目标值和边界值的关系动态的确定

代码实现:

/**
     * @param arr     数组
     * @param left    左索引
     * @param right   右索引
     * @param findVal 查找值
     * @return 如果找到就返回相应的下标。否则返回-1
     */

    public static int insertValSearch(int arr[], int left, int right, int findVal) {
        //注意:findVal < arr[0] 和 findVal > arr[arr.length - 1] 必须需要
        //否则我们得到的 mid 可能越界
        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]);
        if (findVal > arr[mid]) {//说明应该向右索引
            return insertValSearch(arr, mid + 1, right, findVal);
        } else if (findVal < arr[mid]) {//说明应该向左索引
            return insertValSearch(arr, left, mid - 1, findVal);
        } else {
            return mid;
        }
    }

以上代码中:我们要注意:

int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]);

在使用上面代码之前:有个很重要的条件:

        注意:findVal < arr[0] 和 findVal > arr[arr.length - 1] 必须需要
否则我们得到的 mid 可能越界

二者区别:

插值查找算法和二分查找算法的区别主要就在于中间位置mid的确定,它们在终止条件和判断条件上都是相同,在此不做重复


总结

        插值查找对于数列比较大并且比较均匀的数列来说,性能会好很多;但是如果数列极不均匀,则插值查找未必会比二分查找的性能好。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值