斐波那契查找算法详解


斐波那契查找算法详解

说明

  1. 斐波那契查找算法核心思想类似于二分查找和插值查找,区别在于对标志值,即 mid 的设计算法不一样,二分查找直接重用中间值作为标杆,插值查找使用自适应确定mid,而斐波那契查找算法则使用黄金分割,使得mid总是处于查找数列的黄金分割点位置
  2. 因为斐波那契数列越到后边,相邻两数的比值越发接近0.618,也就是黄金分割比,因为可以巧妙的使用斐波那契数列寻找数组中的黄金分割点,即mid对应的下标
  3. 因此需要先构建一个斐波那契数列,可以使用递归的方法或者非递归的方式
  4. 使用斐波那契数列寻找数组的黄金分割点公式为: mid = low + f (k - 1) - 1,k为当前斐波那契数对应的索引
  5. 使用斐波那契数列查找,需要先将当前数组的长度构建为第一个比数组长度大的斐波那契数,这个数对应的索引就是 k ,可以使用循环的方法
  6. 将构建的新数组后边补零的位置替换为数组中的最后一个位置,即最大值
  7. 准备工作准备好后,就可以计算当前数组的黄金分割值,然后获取到当前黄金分割值对应的元素
  8. 将这个元素和要查找的元素进行比较,然后重置左右指针和重置后数组对应的黄金分割点
  9. 当查找完所有的元素后,如果没有找到,则返回 - 1
  10. 注意斐波那契数列的特性 即 当索引 > 2时,当前位置元素 = 前两个位置元素之和,而前两个位置元素之比刚好是满足黄金分割,正是基于这样的特性,才有公式 mid = low + f (k - 1) - 1
  11. 斐波那契查找算法不易理解,须慢慢体会
  12. 源码及详解见下

源码及分析

//斐波那契数列的最大长度
    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 23, 45, 66, 67, 88, 90, 100};
        int index = fisSearch(arr, 88);
        System.out.println("index = " +index);

    }

    //构建斐波那契数列
    public static int[] fis() {
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < f.length; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }
    /**
     * 斐波那契查找算法实现
     *
     * @param arr 要查找的原始数组
     * @param key 要查找的值
     * @return 查找的结果
     */
    public static int fisSearch(int[] arr, int key) {
        //数组左侧索引
        int low = 0;
        //数组右侧索引
        int high = arr.length - 1;
        //比右侧索引大的第一个斐波那契数对应的索引
        int k = 0;
        //黄金分割点
        int mid = 0;
        //斐波那契数列
        int[] f = fis();
        //由数组最大值计算k
        while (high > f[k] - 1) {
            k++;
        }

        //因为f[k]的值可能大于数组的长度,因此需要给原数组扩容到长度 == f(k)
        int[] tmp = Arrays.copyOf(arr, f[k]);
        //调用copyOf方法后在扩容部分全部补了0,实际上需要补数组的最后一位
        for (int i = high + 1; i < tmp.length; i++) {
            tmp[i] = arr[high];
        }
        //使用while循环来查找需要找的数
        while (low <= high) {
            //先计算黄金分割点
            mid = low + f[k - 1] - 1;
            //判断黄金分割点的元素和要查找的元素的关系
            //如果要查找的值在mid左边,重置high和k
            if (tmp[mid] > key){
                high = mid - 1;
                k--;
                //如果要查找的值在mid右边
            }else if (tmp[mid] < key){
                low = mid + 1;
                k -= 2;
                //否则找到该元素
            }else {
                if (mid <= high){
                    return mid;
                }else {
                    return high;
                }
            }
        }
        //如果循环结束后还没有找到,说明没有
        return -1;
    }


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM