(由于本文参考多篇文章,无法注明转载出处, 因此没有标注转载,但在下方注明了所有参考过的网址,特此说明)
3.裴波那契查找法(Fibonacci Search)
3.1)斐波那契数列:
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
如果设F(n)为该数列的第n项(n∈N*),那么这句话可以写成如下形式::F(n)=F(n-1)+F(n-2)
3.2)裴波那契查找(Fibonacci Search)是利用黄金分割原理实现的查找方法。
斐波那契查找的核心是:
1.当key == a[mid]时,查找成功;(但是计算中间位置mid的方法不同)
2.当key < a[mid]时,新的查找范围是low至mid-1, 此时范围个数为F[k-1] - 1个,即数组左边的长度;
3.当key < a[mid]时,新的查找范围是mid+1至high,此时范围个数为F[k-2] - 1个,即数组右边的长度;
解释: 和二分法相似,但是二分法是每次都把数组均为分为二,用的是(mid=(low+high)/2),但是裴波那契查找,也是把数组一分为二,但是左右的长度分别为,左侧F[k-1] - 1个,右侧F[k-2] - 1个(参考原理就是上面提到的F(n)=F(n-1)+F(n-2)),因此计算mid的公式为(mid = low + F[k-1] - 1)。即:折半查找每次将查找表对半分,裴波那契查找就是将拆分方式换成裴波那契数列
下图为示意图
3.3)代码:
/*定义斐波那契查找法*/
int Fibonacci_Search(int *a, int n, int key)
{
int low, high, mid, i, k;
int F[MAXSIZE];
Fibonacci(F); //构造一个斐波那契数组F
low = 1; //最低下标记录为首位
high = n; //最高下标记录为末位
k = 0;
while(n > F[k]-1) //计算n位于斐波那契数列的位置
{
k++;
}
for(i=n; i<F[k]-1; i++) //将a的元素扩展到(某斐波那契数 - 1),即F[k]-1
{
a[i] = a[n];
}
while(low <= high)
{
mid = low + F[k-1] - 1; //计算当前分割的下标
if(key < a[mid])
{
high = mid - 1;
k -= 1;
}
else if(key > a[mid])
{
low = mid + 1;
k -= 2;
}
else
{
if(mid <= n)
return mid; //若相当则说明mid即为查找到的位置
else
return n; //若mid>n则说明是扩展的数值,返回n
}
}
return -1;
}
说明:
1.需要判断现在数组的个数和斐波那契数列的大小关系。如果少要进行扩充,比如斐波那契数列是1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...,但数组的大小是14个元素时,14介于数字13和21之间,也就是k=7和k=8之间,那么需要把数字空充到F【k】-1个,也就是扩充到20个,也需要添加6个元素,然后再来计算mid,才依次进行比较;
2.注意比较完之后,两个k的计算不同,如果要对左侧继续进行比较,则需要k-=1;如果需要右侧,则k-=2
3.4)效率分析:
关于斐波那契查找, 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当众的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂度也为O(logn),但就平均性能来说,斐波那契查找要优于折半查找。可惜如果是最坏的情况,比如这里key=1,那么始终都处于左侧在查找,则查找效率低于折半查找。
还有关键一点,折半查找是进行加法与除法运算的(mid=(low+high)/2),插值查找则进行更复杂的四则运算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low]))),而斐波那契查找只进行最简单的加减法运算(mid = low + F[k-1] - 1),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。
参考:https://www.cnblogs.com/praglody/p/6739989.html
https://blog.csdn.net/qq_16234613/article/details/52690150
https://baike.sogou.com/v267267.htm?fromTitle=斐波那契数列
https://blog.csdn.net/zpluw/article/details/7757331