浅谈:斐波那契搜索算法(Fibonacci search)

一:有趣的背景

​ 谈到斐波那契查找算法,总是有一个神奇的数字与之紧密相连——黄金分割数(0.618)。黄金分割数被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。在斐波那契数列中,也存在着黄金分割数的身影。我们不妨在斐波那契数列中寻找一下它的身影。

斐波那契数列:0 1 1 2 3 5 8 13 21 34 55 89 144 233 377…

1/2 = 0.5; 2/3 = 0.667; 3/5 = 0.6

5/8 = 0.625; 8/13 = 0.615; 13/21 = 0.619

21/34 = 0.617; 34/55 = 0.618;…

​ 所以在斐波那契数列的指引下,通过斐波那契查找算法分割时,分割后的两段也会呈现出黄金分割的特点(这可真是计算机大佬们的硬核审美啊~~),但是人们发明这个算法可并不是为了好看,一定是有它的优点,这留到我后面再说。

二:算法原理
1. 前提条件

首先我们得知道,斐波那契查找算法是折半查找算法的一个提升算法,所以它以一定要在折半查找的前提下进行。所以,要使一个表能够用斐波那契查找,首先它得是一个顺序存储的有序表

2. 算法描述(设有序表为a)

​ 斐波那契查找是依据斐波那契序列的特点对表进行分割的。假设开始时表中记录的个数(不妨设为n)比某个斐波那契数(Fu)小1,即 n = Fu - 1(这也是一个前提条件),然后将给定值 key 和 a[Fu-1] 进行比较

  • 若相等,则查找成功
  • 若key < a[Fu-1] ,则继续在 a[1] 至 a[Fu-1 - 1] 的子表中进行查找
  • 若key > a[Fu-1] ,则继续在 a[Fu-1 + 1] 至 a[Fu - 1] 的子表中进行查找。该子表的长度为 Fu-2 - 1
3. 算法剖析

为了更加直观的理解斐波那契查找的过程,我们借助上图进行一个简单的分析,按①~③的顺序。

首先我们生成一个斐波那契数列: F1 = 1, F2 = 1, F3 = 2, F4 = 3, F5 = 5, F6 = 8, F7 = 13;

然后我们设,有序表a, 从a[1]~a[12] 的值为 1 ~ 12。(为了方便理解,储存该表的数组的a[0]为空)

我们假定,需要查找的数为key = 4。

因为 n = Fu - 1 ,可以知道此时,u = 7。将key和a[F7-1] (即a[8])进行比较,我们发现key<a[8]。

然后在a[1]~a[7]中进行查找,此时u = 6。将key和a[F6-1](即a[5])进行比较,我们发现key<a[5]。

然后再a[1]~a[4]中进行查找,此时u = 5。将key和a[F5-1](即a[3])进行比较,我们发现key>a[3]。

此时只剩a[4],查找完毕。

4. 算法优点

斐波那契查找的平均性能比折半查找好,但最坏情况下(虽然时间复杂度仍为O(logn))的性能却比折半查找差。它还有一个有点就是分割时只需加减运算,而二分法需要除2.

三:算法实现
// Fibonacci_Search.c

#include <stdio.h>

#define MAX_SIZE 20
#define MAX 100

// ----构造斐波那契数列-----
void Fibonacci(int *F) {
    F[0] = 0;
    F[1] = 1;
    for (int i = 2; i < MAX_SIZE; ++i) {
        F[i] = F[i - 1] + F[i - 2];
    }
}

//----斐波那契查找算法------
int Fibonacci_Search(int *a, int n, int key) {     //a为需要查找的数组,n为a的长度,key为要查找的数
    int F[MAX_SIZE];
    Fibonacci(F);
    int u = 0;
    while (n > F[u]) {						//计算n位于斐波那契数列的位置
        u ++;
    }
    for (int i = n; i < F[u] - 1; ++i) {	//如果n < F[u] - 1,则用数组最后一个数补位,直到n = F[u] -1
        a[i + 1] = a[i];
    }
    int low = 1;
    int high = n;
    while (low <= high) {
        int mid = low + F[u - 1] - 1;
        if (mid > n && a[n] == key) {		//若mid>=n则说明是扩展的数值,返回n
            return n;
        } else if (mid > n){
            return -2;						//查找的数据不存在
        }
        if (key == a[mid]) {
            return mid;
        } else if (key > a[mid]) {
            low = mid + 1;
            u -= 2;
        } else if (key < a[mid]) {
            high = mid - 1;
            u--;
        }
    }
    return -2; 								//查找的数据不存在
}

int main() {
    int F[MAX_SIZE];
    Fibonacci(F);
    int a[MAX];
    int n;
    printf("请输入数据的个数:");
    scanf("%d", &n);
    printf("请输入数据:\n");
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    int key;
    printf("请输入要查找的数据:");
    scanf("%d", &key);
    int answer = Fibonacci_Search(a, n, key);
    printf("数据位于第%d项\n", answer);
    return 0;
}
四:运行结果

参考文献:
[1]: 严蔚敏数据结构c语言版 221页
[2]: 云中孤鹜csdn博客 https://blog.csdn.net/yunzhongguwu005/article/details/9341761
[3]: 百度百科

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值