斐波那契查找实现及原理

斐波那契查找原理

斐波那契查找是一种在有序表中高效查找指定元素的算法,比折半查找要复杂一些,主要复杂在要多做不少准备工作。下面看它的工作流程:

  1. 计算并保存一个斐波那契序列的数组 F[ ] = {1, 2, 3, 5, 8, 13, 21},方便以后取值。
  2. 把有序数组的长度扩充到 nums. length = F[k] - 1,k 是满足条件的最小值,比如数组长度为15,那么就把它长度扩充到 F[6] - 1 = 20,所有在末尾添加的扩充元素都是原数组最后一个元素的复制。
  3. 找到 mid 元素,不断进行二分比较,直到找到目标元素为止,这一步的做法与折半查找很相似,仅仅是计算 mid 的公式从 (low + high) / 2改为 low + ( F[k-1] - 1)。

斐波那契查找的理解难点就一个:为什么需要把数组长度扩充到 F[k] - 1 而不是 F[k] 或者 F[k+1]?
其实这是为了能正确递归计算mid值,看下图可发现 F[k]-1 = (F[k-1] + F[k-2]) - 1 = (F[k-1]-1) + 1 + (F[k-2]-1),中间的1就是我们二分的锚点mid,如果目标在左区,数组长度就缩到(F[k-1]-1),如果在右区,数组长度就缩到(F[k-2]-1),否则就等于mid完成查找。而(F[k-1]-1)又能拆成(F[k-2]-1)+1+(F[k-3]-1),这样递归分割下去就能不断的缩小区间直至找到目标。
对数组的划分
代码实现:

	public int fibonacciSearch( int[] nums, int target) {
        //设定一个斐波那契数组
        int Fibonacci[] = {1, 2, 3, 5, 8, 13};
        int k = 0;
        while(nums.length >= Fibonacci[k]) {
            k++;
        }
        //若nums数组长度不够Fibonacci[k]-1,用末尾元素补齐
        int extra_length = Fibonacci[k] - nums.length - 1;
        int newArr[] = new int[nums.length+extra_length];
        for(int j=0; j<nums.length; j++) {
            newArr[j] = nums[j];
        }
        for(int i=nums.length; i<newArr.length; i++) {
            newArr[i] = nums[nums.length-1];
        }
        int low, high;
        low = 0;
        high = newArr.length-1;
        //开始斐波那契查找
        while(low <= high) {
            int mid = low + Fibonacci[k-1]-1;
            if(target < newArr[mid]) {
                high = mid - 1;
                k -= 1;
            }else if(target > newArr[mid]) {
                low = mid + 1;
                k -= 2;
            }else {
                if(mid < nums.length) {
                    return mid;
                }else {
                    return nums.length - 1;
                }
            }
        }
        return -1;
    }

与折半查找的性能比较

二者理论效率半斤八两,时间复杂度都是log2n,有人说斐波那契查找比折半查找效率高,理由有2个:1.斐波那契查找只用到加减法,而折半查找计算mid时要除以2,除法很影响效率;2.如果目标在low->mid区,只需要判断一次,而如果在mid->high需要判断2次(需要先判断不在low->mid区,再判断在mid->high区),斐波那契查找的low->mid区更大(0.618>0.5),有更多的概率只需要判断一次,所以总体判断次数更少。

原因1看起来有理,可是现在的编译器只要遇到/2操作,都会优化为>>1,位运算比加减法只快不慢,所以原因1不成立。原因2也貌似有些道理,可是如果按照这个道理推理下去,把分割点设在0.99岂不是更好?可0.99明显是个垃圾分割点,二分力度很差,所以这个理由我也不认可。

斐波那契查找的时间复杂度还是O(log 2 n ),但是 与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是还是得视具体情况而定。

本文转载自https://blog.csdn.net/weixin_42145502/article/details/99676008
本人在此基础上做了一点修改和取舍,修改仅代表本人观点,与原作者无关。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值