转自:云中孤鹜 http://blog.csdn.net/yunzhongguwu005/article/details/9341761
斐波那契查找的前提是待查找的查找表必须顺序存储并且有序。
相对于折半查找,一般将待比较的key值与第mid=(low+high)/2位置的元素比较,比较结果分三种情况
1)相等,mid位置的元素即为所求
2)> ,low=mid+1;
3) < ,high=mid-1;
斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=Fk-1;
开始将k值与第F(k-1)位置的记录进行比较(及mid=low+F(k-1)-1),比较结果也分为三种
1)相等,mid位置的元素即为所求
2)> ,low=mid+1,k-=2;
说明:low=mid+1说明待查找的元素在[mid+1,hign]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找
3)< ,high=mid-1,k-=1;
说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1
个,所以可以递归 的应用斐波那契查找
斐波那契查找的算法如下:
- // 斐波那契查找.cpp
- #include "stdafx.h"
- #include <memory>
- #include <iostream>
- using namespace std;
- const int max_size=20;//斐波那契数组的长度
- /*构造一个斐波那契数组*/
- 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为要查找的数组长度,key为要查找的关键字
- {
- int low=0;
- int high=n-1;
- int F[max_size];
- Fibonacci(F);//构造一个斐波那契数组F
- int k=0;
- while(n>F[k]-1)//计算n位于斐波那契数列的位置
- ++k;
- int * temp;//将数组a扩展到F[k]-1的长度
- temp=new int [F[k]-1];
- memcpy(temp,a,n*sizeof(int));
- for(int i=n;i<F[k]-1;++i)
- temp[i]=a[n-1];
- while(low<=high)
- {
- int mid=low+F[k-1]-1;
- if(key<temp[mid])
- {
- high=mid-1;
- k-=1;
- }
- else if(key>temp[mid])
- {
- low=mid+1;
- k-=2;
- }
- else
- {
- if(mid<n)
- return mid; //若相等则说明mid即为查找到的位置
- else
- return n-1; //若mid>=n则说明是扩展的数值,返回n-1
- }
- }
- delete [] temp;
- return -1;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- int a[] = {0,16,24,35,47,59,62,73,88,99};
- int key=100;
- int index=Fibonacci_Search(a,sizeof(a)/sizeof(int),key);
- cout<<key<<" is located at:"<<index;
- system("PAUSE");
- return 0;
- }
斐波那契查找的核心是:
1)当key=a[mid]时,查找成功;
2)当key<a[mid]时,新的查找范围是第low个到第mid-1个,此时范围个数为F[k-1] - 1个,即数组左边的长度,所以要在[low, F[k - 1] - 1]范围内查找;
3)当key>a[mid]时,新的查找范围是第mid+1个到第high个,此时范围个数为F[k-2] - 1个,即数组右边的长度,所以要在[F[k - 2] - 1]范围内查找。
关于斐波那契查找, 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当众的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂度也为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),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。
参考文献:严蔚敏数据结构c语言版 221页 及芦苇csdn博客 http://blog.csdn.net/zpluw/article/details/7757331
斐波那契查找算法解析
大家记不记得斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和),然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。大家请看:
斐波那契查找(Fibonacci Search):基于折半查找,对于mid的选择,使用斐波那契数组进行了调整。
mid计算公式:mid = low + F[k - 1] - 1
斐波那契数组计算公式:
F[K] = F[k - 1] + F[K - 2]
F[k] - 1 = (F[k - 1] - 1) + 1 + (F[k - 2] - 1)
对于二分查找,分割是从mid=(low+high)/2开始;而对于斐波那契查找,分割是从mid = low + F[k-1] - 1开始的;通过上面知道了,数组a现在的元素个数为F[k]-1个,即数组长为F[k]-1,mid把数组分成了左右两部分,左边的长度为:F[k-1] -1,那么右边的长度就为(数组长-左边的长度-1),即:(F[k]-1)- (F[k-1] -1)- 1 =F[k] - F[k-1] - 1 = F[k-2] - 1。
斐波那契查找的核心是:
1)当key=a[mid]时,查找成功;
2)当key<a[mid]时,新的查找范围是第low个到第mid-1个,此时范围个数为F[k-1] -1个,即数组左边的长度,所以要在[low,F[k - 1] - 1]范围内查找;
3)当key>a[mid]时,新的查找范围是第mid+1个到第high个,此时范围个数为F[k-2] -1个,即数组右边的长度,所以要在[F[k -2] - 1]范围内查找。
优劣势分析:mid左侧的元素个数大于右侧的元素个数。所以,如果要查找的记录在右侧,则左侧的数据就不需要比较了,效率比较高;如果要查找的记录在左侧,则效率比较低。
还有关键一点,折半查找是进行加法与除法运算的(mid=(low+high)/2),插值查找则进行更复杂的四则运算(mid = low + (high - low) * ((key - a[low]) / (a[high] -a[low]))),而斐波那契查找只进行最简单的加减法运算(mid = low + F[k-1] - 1),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。
下面来看下斐波那契查找代码实现:
- /*斐波那契查找法,前提是线性表必须有序,时间复杂度是O(logn)*/
- #include<iostream>
- const int MAXSIZE = 20;
- int Fibonacci_Search(int *a, int n, int key);
- /*用非递归法构造一个斐波那契数组*/
- void Fibonacci(int *f)
- {
- f[0] = 1;
- f[1] = 1;
- for(int i=2; i<MAXSIZE; i++)
- {
- f[i] = f[i-1] + f[i-2];
- }
- }
- int main()
- {
- int array[] = {0,16,24,35,47,59,62,73,88,99};
- int number = Fibonacci_Search(array, sizeof(array)/sizeof(int), 73);
- std::cout<<"位置是:array["<<number<<"]\n";
- return 0;
- }
- /*定义斐波那契查找法*/
- 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;
- }