7.4-斐波那契查找:通俗易懂理解

package com.hejiale.arithmetic.search;

import java.util.Arrays;

/*
    斐波那契查找算法也是针对二分查找的,只不过类似于插值查找,我们又是修改的求mid的公式
        1.  首先,说明白斐波那契数列,斐波那契数列的特点是,当下标大于等与2的时候,f[k]=f[k-1]+f[k-2],首先给一个
            斐波那契数列方便理解
            {1    1    2    3    5    8    13    21    34    55    89    144    233}
            我们可以发现:2=1+1   8=5+3    21=13+8    233=144+59
            那么我们就可以联想到一点,如果我们的数组,长度等于斐波那契数列中的值,那么一定可以将其拆分成两部分
            比如数组长度为8,那么我们可以通过斐波那契数组,将其分为两部分,长度分别为3,和5,既然我们可以将数组分
            成两部分,就可以联想到我们之前的二分查找和插值查找,因为他们归根到底,也是把数组分成两部分递归查找的
        2.  但是实际开发中,我们的数组的长度,不一定刚刚好是斐波那契数列中的值,那么这个时候,我们应该对原始数组
            进行扩充,直到长度等于斐波那契数列中的值,然后将数组拆分即可
        3.  接下来,我们应该确定的就是mid的取值
            mid = low + f[k-1]-1
            为什么呢?
                1.  首先,low是左边界的值,不能是0,因为涉及到递归,我们可能递归的是左部分,这个时候用0就是错误的,所以
                    应该是low而不是0
                2.  其次,f[k-1]是因为,我们的f[k]=f[k-1]+f[k-2],所以f[k-1]其实就是整个数组的分割点,比如8=3+5
                    那么3/5就是分割点,我们可以让f[k-1]作为分割点,也可以让f[k-2]作为分割点
                3.  至于-1,因为我们f[k]涉及的是长度,要换成下标,就应该-1
        4.  那么我们的开发步骤就清晰了
            1.  准备一个合适长度的斐波那契数列
            2.  如果数组的长度,不是斐波那契数列中的值,我们就扩充数组长度,直到找到对应的斐波那契数列的值,扩充的方法
                就是对数组的最后一个元素的复制
            3.  数组长度也准备好后,接下来就是核心代码,准备一个while循环(low <= high)
                1.  如果key < temp[mid],说明我们应该在左部分继续查找
                        high = mid-1;
                        k--;
                        k--这里不好理解,首先,我们的mid=low+f[k-1]-1;也就是说,如果数组长度为f[5]8,那么左半部分是5个元素
                        右半部分是3个元素,那么左半部分是五个,所以我们针对的就是这五个,f[4] =5,所以是k--
                2.  如果key > temp[mid],说明我们应该在右部分继续查找
                    low = mid+1;
                    k-=2;
                    那么这里我们还是假设数组长度为8,那么我们在右半部分查找,右半部分就应该有3个元素,f[3]=3,所以说是k-=2
                3.  如果key = temp[mid],说明查找成功,返回mid即可
        5.  总结:斐波那契查找的好处是,算法中mid的取值不涉及到/*,只涉及加减,效率会得到一定的提升
*/
public class FibonacciSearch {

    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        /*int[] fib = fib();
        for (int i : fib) {
            System.out.print(i+"    ");
        }*/
        System.out.println(fibSearch(arr, 12));

    }

    //因为后面的mid = low + F(k-1) -1 需要用到斐波那契数列,所以我们要先构建一个斐波那契数列
    //非递归方式得到斐波那契数列
    public static int[] fib() {
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < maxSize; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }


    //斐波那契查找算法
    //使用非递归的方式编写算法

    /**
     * @param a   被查找数组
     * @param key 查找的关键字
     * @return 返回对应下标,-1代表查找失败
     */
    public static int fibSearch(int[] a, int key) {
        int low = 0;
        int high = a.length - 1;
        int k = 0;//表示斐波那契分割数值的下标
        int mid = 0;//存放mid的值
        int[] f = fib();//获得到斐波那契数列
        //获取到斐波那契分割数值的下标
        while (high + 1 > f[k]) {
            k++;
        }
        /*
            f[k]对应的值不一定和数组大小相同,有可能大于数组长度,如果大于,我们就要填充数组,直到长度相等,填充的就是
            数组的最后一个元素
        */
        int[] temp = Arrays.copyOf(a, f[k]);
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = a[high];
        }
        //使用while来循环处理,找到key,当low>high的时候跳出循环
        while (low <= high) {
            mid = low + f[k - 1] - 1;
            if (key < temp[mid]) {//说明应该继续向数组的前部分查找
                high = mid - 1;
                k--;
            } else if (key > temp[mid]) {//说明应该向数组的后半部分查找
                low = mid + 1;
                k -= 2;
            } else { // 找到了
                // 返回下标,需要确定返回的下标,因为我们之前,对数组进行了扩充
                if (mid <= high) {
                    return mid;
                } else {
                    return high;
                }
            }
        }
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值