查找算法java实现——斐波那契(黄金分割法)查找

斐波那契(黄金分割法)查找

一、基本思路

在介绍斐波那契查找之前,让我们先了解几个基本的概念:

  1. 黄金分割点:是指把一条线段分成两部分,使其中一部分与全长之比等于另一部分与这部分之比,其比值保留三位小数约为0.618。由于按此比例设计的造型十分美丽,因此成为黄金分割,也称为中外比。
  2. 斐波那契数列:{1,1,2,3,5,8,13,21,34,55…}即相邻两个数的和等于这两个数后面的数,通过计算我们可以发现,斐波那契数列中的两个相邻数的比例,无限接近黄金分割值0.618。

要注意的是:斐波那契查找和前面的两个查找方式类似,也要求待查找的序列是有序的。
基本思路: 斐波那契查找与二分查找和插值插值相似,只不过改变了划分的节点,不再是中点和插值点,而是位于黄金分割点附近,所以我们需要利用斐波那契数列的特性来找到这个黄金分割点。
在这里插入图片描述

  1. 由斐波那契数F [ k ]= F [ k - 1] + F[ k - 2 ]的性质,可以得到( F [k] - 1 ) = ( F [ k - 1] - 1 ) + ( F [ k - 2 ] - 1 ) + 1 。该式说明只要顺序表的长度为F[ k ]- 1 ,则可以将该表分成长度为 F[k - 1 ]- 1 和 F [ k - 2] - 1 的两段,即如图所示。从位置为 mid = low + F ( k - 1 )- 1)
  2. 类似的,每一子段也可以用相同的方式分割
  3. 但顺序表长度 n 不一定刚好等于 F[k]- 1 ,所以需要将原来的顺序表长度 n 增加至 F[k]- 1 。这里的 k 值只要能使得 F[k]-1恰好少于或等于 n 即可,由以下代码得到,顺序表长度增加后,新增的位置(从 n + 1到 F[k]- 1 位置),都赋值为 n 位置的值即可。
    while ( n > f[k]-1)
    k++;
    简单来说: 就是根据待排序列的个数,利用斐波那契数列找到一个大于等于序列个数的数,那么这个数前面两个数之比就是分割比例。

二、算法分析

斐波那契查找可以使用递归实现,也可以使用循环实现。
时间: 当用递归实现时,根据递归树的高度,可得时间复杂度O( 2 n 2^n 2n),当用循环实现时,时间复杂度位O(n)
空间: 当用递归实现时,空间复杂度O(n),当用循环实现时,空间复杂度位O(n)

算法平均时间最好情形最差情形空间复杂度备注
斐波那契查找O(n)O(n)O( 2 n 2^n 2nO(n)待查表是有序表

三、代码实现

这里采用循坏来实现:

import java.util.Arrays;

/**
 * @author dankejun
 * @create 2020-05-03 14:49
 */
public class FibonaciSearch {
    public static int maxSize = 20;
    public static void main(String[] args) {
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        System.out.println("下标为:" + fibSearch(arr,89));
    }

    //非递归得到一个斐波那契数列
    public static int[] fib() {
        int[] f = new int[maxSize];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < f.length; i++) {
            f[i] = f[i - 2] + f[i - 1];
        }
        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;
        int[] f = fib();
        //获取到斐波那契分割数值的下标
        while (high > f[k] - 1) {//未找到
            k++;
        }
        int[] temp = Arrays.copyOf(a, f[k]);
        for (int i = high+1; i < temp.length; i++) {//将空的位置补齐
            temp[i] = a[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;
    }
}

测试序列: int arr[] = {1, 8, 10, 89, 1000, 1234};查找是否含有元素89
测试结果:
在这里插入图片描述
测试序列: int arr[] = {1, 8, 10, 89, 1000, 1234};查找是否含有元素34
测试结果:
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值