有序查找之:斐波那契(fibonacci)查找(C语言)
关于此算法的理解
斐波那契序列一些要牢记的知识点
序列从第3个值开始,其值等于前2个值的和
? 即某值下标为k,则F[k] = F[k-1] + F[k-2]
相临的序列的值 前面的值/后面的值会越来越接近0.618黄金分割点
为什么会有斐波那契查找法?
既然二分查找法可以找到一个序列的0.5的位置进行查找,那么斐波那契也可以使用它的黄金分割点0.618位置应用于序列查找(如果有白银分割点0.718那么也可能也会有白银查找法)
相比二分查找,该算法的优势是在求mid值的时候使用的只是加减法,在数量较大时会提高效率
斐波那契序列数组和待查找的数组有什么关联性?
根据待查询数组的查询长度(以下简称查询长度),生成斐波那契序列数组,且该序列的值一定要把查询长度包含进来,也就是说比如查询长度为n,则斐波那契序列只要满足最后一个下标的值F[k], n <= f[k]就可以,太长无用,序列第1个值是不是0无所库(本次实现直接手动创建fibonacci)
拉下来根据待查询数组的查询长度n匹配斐波那契序列,获取关键下标k,有两种情况:
? n 正好是有效的斐波那契序列中的数字n = F[k],直接返回所在下标k
? n 不是有效非斐波那契序列数字n < F[k],同样返回k,只是出现这种情况后原数组需要把查询长度扩充到k的长度才以进行查询,新扩充的位置用待查询数组的最后一个元素填充
F[k-1] 和 F[k-2] 两者的数量正好是被查询数组使用黄金分割点后的两部分
实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int fibonacci(int *, int, int);
int fibonacci(int *a, int n, int key)
{
int low, high, mid, k;
low = 1;
high = n;
// 为了代码简化直接手动创建fibonacci序列数组,如果代码动态生成则只要序列的最后1个值>=n即可
// 此数组的值第1个元素是0还是1感觉影响不大
int F[] = {0, 1, 1, 2, 3, 5, 8, 13}; // 这些值可以理解为使用fibonacci分割了查询长度
k = 0;
while (n > F[k]) { // 书为为n > F[k] - 1,但是感觉不需要,如果加上-1, n=8那么k最终为7 值13,完全用不上
k++; // 返回下标7, 值为13, 8 < 10 < 13 向后取大
}
// 如果F[k]>n,则a数据扩容(扩容后的长度等于F[k] + 1,1为待查询数组不使用0下标)
if (F[k] > n) {
a = realloc(a, sizeof(int) * (F[k]+1));
for (int i=n+1; i<=F[k]; i++) { // 11, 12, 13执行3次,填充新的数组元素
a[i] = a[n];
}
}
// 下面这段可以去掉注释看效果
//if (F[k] > n) {
// printf("a数组查询长度需要扩展到 %d\n", F[k]);
// for (int i=0; i<=F[k]; i++) { // 下标14已经无效,这里为了看效果
// printf("a[%d]=%d\n", i, a[i]);
// }
/