数据结构与算法--斐波那契查找

斐波那契数列

斐波那契数列,又称黄金分割数列,因数学家莱昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……

在数学上,斐波那契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*),该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)


斐波那契查找算法简介

斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数 F[n],将原查找表扩展为长度为 F[n](如果要补充元素,则补充重复最后一个元素,直到满足 F[n] 个元素),完成后进行斐波那契分割,即 F[n] 个元素分割为前半部分 F[n-1] 个元素,后半部分 F[n-2] 个元素,找出要查找的元素在那一部分并递归,直到找到


算法图解

斐波那契查找原理与二分查找算法和插值查找算法类似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近

公式 mid= left + F(k-1) - 1,(F代表斐波那契数列,k代表斐波那契数列分割数值的下标)

公式推导

 


代码实现

import java.util.Arrays;

public class FibSearch {

    //假定斐波那契数列大小为20
    public static int maxSize = 20;

    public static void main(String[] args) {
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        System.out.println("index= " + fibSearch(arr, 1000));
    }

    /**
     * 获取一个斐波那契数列
     */
    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 arr    数组
     * @param target target需要查找的值
     * @return 返回对应下标,未找到返回-1
     */
    public static int fibSearch(int[] arr, int target) {

        int left = 0;
        int right = arr.length - 1;
        //表示斐波那契分割数值的下标
        int k = 0;
        //存放mid值
        int mid = 0;

        //获取斐波那契数列
        int[] f = fib();

        //获取斐波那契分割数值的下标
        while (right > f[k] - 1) {
            k++;
        }

        //因为f[k],可能大于数组长度,因此需要补齐,不足部分使用0填充
        int[] temp = Arrays.copyOf(arr, f[k]);
        //用数组最后的数值填充temp
        //举例:temp={1, 8, 10, 89, 1000, 1234,0,0} => {1, 8, 48, 88, 89, 899, 1024,1024,1024}
        for (int i = right + 1; i < temp.length; i++) {
            temp[i] = arr[right];
        }

        //使用while循环找到target
        while (left <= right) {
            //得到mid的下标
            mid = left + f[k - 1] - 1;

            if (target < temp[mid]) {
                //继续向数组前部分查找(左边)
                right = mid - 1;
                //为什么是k--
                //说明:
                //1.全部元素=前边元素+后边元素
                //2.f[k] = f[k-1] + f[k-2]
                //因为前面的 f[k-1] 个元素,所以可以继续拆分,f[k-1] = f[k-2] + f[k-3]
                //即再f[k-1]的前面继续查找,k--
                k--;
            } else if (target > temp[mid]) {
                //继续向数组后部分查找(右边)
                left = mid + 1;
                //为什么是k-2
                //说明:
                //1.全部元素=前边元素+后边元素
                //2.f[k] = f[k-1] + f[k-2]
                //3.后面右 f[k-2] 个元素,继续拆分
                //即下次循环 mid = f[k-1-2] - 1
                k -= 2;
            } else {
                //找到。需要确定,返回哪个值
                if (mid <= right) {
                    return mid;
                } else {
                    return right;
                }
            }
        }

        //未找到
        return -1;
    }
}
posted @ 2022-07-30 23:00  伊文小哥  阅读(161)  评论(0编辑  收藏  举报