• 查找算法(二)插值查找


    二分查找(折半查找)

    它的前提是线性表中的记录必须是有序的,线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。

    代码实现
    int BinarySearch(int a[], int value){
        int n = a.length;
        int low = 0;
        int high = n-1;
        while(low <= high){
            int mid = low + (high - low)/2;
            if(a[mid] == value){
                return mid;
            }
            elseif(a[mid] > value){
                high = mid -1;
            }
            else{
                low = mid + 1;
            }
        }
        return -1;
    }
    

    递归版本

    int BinarySearch(int a[], int value, int low, int high){
        int mid = low + (high - low)/2;
        if(a[mid] == value){
            return mid;
        }
        elseif(a[mid] > value){
            return BinarySearch(a,value,low,mid - 1);
        }else{
            return BinarySearch(a,value,mid + 1,high);
        }
    }
    

    时间复杂度:O(log(n))

    注:折半查找的前提条件是需要有序顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用了。

    插值查找

    基本思想:基于二分查找,将查找点的选择改进为自适应选择,可以提高查找效率。 将二分查找的插值计算公式改为

    mid = low + frac{(value - a[low])}{(a[high] - a[low])}(high-low)mid=low+(a[high]a[low])(valuea[low])(highlow)

    时间复杂度:平均情况O(loglog(n)),最坏O(log(n))

    注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找的平均性能比折半查找要好。

    斐波那契查找

    原理:斐波那契查找与折半查找很相似,他是根据斐波那契数列的特点对有序表进行分割的。他要求开始表中记录的个数比某个斐波那契数小1,即n=F(K)-1;当记录不满足时,后面的元素都赋值为最后一个值。(注:使得n=F(K)-1是因为:如果表中的数据为F(K)-1个,mid分割又用掉一个,剩下F(K)-2个,正好分给两个子序列,每个子序列的长度为F(K-1)-1和F(K-2)-1,格式和之前的统一,方便递归编程实现)

    mid = low + F(k-1) - 1mid=low+F(k1)1

    将value值与第mid位置的记录进行比较,比较结果分为三种:

    1. 相等,则返回mid
    2. 大于,low = mid + 1low=mid+1,k = k - 2k=k2
    3. 小于,high = mid - 1high=mid1,k = k - 1k=k1

    (说明:low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为F(k-2)-1个)

    代码实现
    int FibonacciSearch(int[] a, int value){
    
        int[] F = {0,1,1,2,3,5,8,13,21,34,55};
        int n, low, high, mid, k;
        n = a.length;
        low = 1;
        high = n;
        k = 0;//斐波那契数列的下标
        while(n > F[k] - 1){//计算n位于斐波那契数列的位置
            ++k;
        }
        //省略将a后移一位,并且长度扩展到F(k)
        for(int i = n + 1; i <= F[k] - 1; i++){//将不满的数值补全,长度为F(k) - 1
            a[i] = a[n];
        }
        while(low <= high){
            mid = low + F[k - 1] - 1;
            if(value > a[mid]){
                low = mid + 1;
                k = k - 2;//mid右边的个数,即F[k-2]个
            }elseif(value < a[mid]){
                high = mid - 1;
                k = k - 1;//mid左边的个数
            }else{
                if(mid < n){
                    return mid;
                }else{
                    return n;
                }
            }
        }
        return -1;
    }
    
    

    总结: 平均性能平均性能:斐波那契>折半>插值

    因为折半查找进行加法与除法运算(mid = (low + high) / 2),插值查找进行复杂的四则运算( mid = low + (key - a[low] / (a[high] - a[low]) * (high - low)) ),而斐波那契查找只是运用简单加减法运算 (mid = low + f[k-1] -1) ,在海量的数据查找过程中,这种席位的差别会影响最终的查找效率。三种有序表的查找本质上是分割点的选择不同,各有优劣,实际开发可根据数据的特点综合考虑再做决定。

  • 相关阅读:
    php生成excel
    gearmand
    开启Nginx的目录文件列表功能
    zend框架学习
    引用方法形成树
    智能指针实现
    图文例解C++类的多重继承与虚拟继承
    CC++定位崩溃代码行的方法
    C++函数重定义、重载、重写
    勤奋吧,一天一点,努力提高基本技能。
  • 原文地址:https://www.cnblogs.com/small-boy/p/8029205.html
Copyright © 2020-2023  润新知