折半查找、插入查找、斐波那契查找

折半查找

前提:数据有序。

思想:将key与数组中间值arr[middle]比较,若key>arr[middle],说明目标值在数组右边部分,继续在数组右半边查找,同理,key<arr[middle],继续在数组左半边查找。重复以上过程,直至key = arr[middle]。找到目标数据。

时间复杂度:O(logn)

/**
     * @return 关键字在数组中的位置
     */
    public static int b_search(int[] arr,int key){
        int location = -1;
        int length = arr.length;
        int low = 0;
        int high = length - 1;
        int mid;
        //需要取=,否则最后一个数据找不到
        while (low <= high){
            mid = (low+high)/2;
            if (key < arr[mid]){
                high = mid - 1;

            }else if (key > arr[mid]){
                low = mid + 1;
            }else {
                location = mid;
                break;
            }
        }
        return location;
    }

插值查找

为什么一定要折半而不是折四分之一或者折更多?

eg. 1 在英文字典中查找“apple”,我们会从靠前的位置开始查找。查“zoo”会从靠后的位置查找。而不会从中间开始查起。

eg.2 在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。

所以插值查找与折半查找的不同处在于每次不是从中间切分,而是根据要查找的关键字key与查找表中的最大最小记录的关键字比较后,使middle的选择尽量靠近key。

折半查找中:middle = (low + high)/2;

插值查找:middle = low + [(key - arr[low])/arr[hign] - arr[low]]/(hign - low)

时间复杂度:O(logn),对数据较多且关键字分布较均匀的场景,插值查找的平均性能优于折半查找。

斐波拉契查找

斐波拉契数列:1,1,2,3,5,8,13,21,34,55,89 ...

利用斐波拉契数列F(n) = F(n-1) + F(n-2)  (其中,n >=2)的性质来分割数组。即当前位置值 = 前两个数之和。2=1+1,3=2+1,5=3+2 ...

每次分割,数据总个数 = F(n),左边数据个数 = F(n-1),右边数据个数F(n-2)。n表示斐波拉数列的下标。

时间复杂度O(logn)

    /**
     * @return 关键字在数组中的位置
     */
    public static int fibonacci_search(int[] arr,int key){
        int[] f = {1,1,2,3,5,8,13,21,34,55,89};//斐波拉契数,实际应用中可以抽取一个函数,通过传参构建一个特定长度的斐波拉契数列
        int location = -1,mid,k=0,length = arr.length,low = 0,high = length-1;
        //找到一个最小的斐波拉契数,使数组的长度<=该数。即若数组长度是4,该数是5,数组长度是8,该数是8
        while (length > f[k]){
            k++;
        }
        //当找到的斐波拉契数>数组长度,需要将多出的数据位填上数据(填数组中最大的数)
        int last_v = arr[length-1];//最后一个数据值
        //将数组扩展到f[k]长度
        int[] temp = Arrays.copyOf(arr, f[k]);
        //填充
        for (int i = length; i < f[k];i++){
            temp[i] = last_v;
        }

        //左边:0,f(k-1)-1;右边f(k-1),f[k]-1
        while (low <= high){
            mid = low + f[k-1] - 1;//减1是因为下标从0开始。
            if (key < temp[mid]){
                high = mid -1;
                k = k-1;//在左部分继续找
            }else if (key > temp[mid]){
                low = mid + 1;
                k = k-2;//在右部分继续找,下标mid+1~f(k)-1
            }else {
                if (mid < length-1){
                    location = mid;
                }else {
                    //mid>length-1, 说明找到的是补全的数
                    location = length-1;
                }
                break;
            }
        }
        return location;
    }

参考:《大话数据结构》

猜你喜欢

转载自blog.csdn.net/xybz1993/article/details/80558811