斐波那契查找详解

来源:互联网 发布:做网络销售工资怎么样 编辑:程序博客网 时间:2024/04/30 02:31

转自:云中孤鹜 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

 

个,所以可以递归 的应用斐波那契查找


斐波那契查找的算法如下:

[html] view plain copy
  1. // 斐波那契查找.cpp   
  2.   
  3. #include "stdafx.h"  
  4. #include <memory>  
  5. #include  <iostream>  
  6. using namespace std;  
  7.   
  8. const int max_size=20;//斐波那契数组的长度  
  9.   
  10. /*构造一个斐波那契数组*/   
  11. void Fibonacci(int * F)  
  12. {  
  13.     F[0]=0;  
  14.     F[1]=1;  
  15.     for(int i=2;i<max_size;++i)  
  16.         F[i]=F[i-1]+F[i-2];  
  17. }  
  18.   
  19. /*定义斐波那契查找法*/    
  20. int Fibonacci_Search(int *a, int n, int key)  //a为要查找的数组,n为要查找的数组长度,key为要查找的关键字  
  21. {  
  22.   int low=0;  
  23.   int high=n-1;  
  24.     
  25.   int F[max_size];  
  26.   Fibonacci(F);//构造一个斐波那契数组F   
  27.   
  28.   int k=0;  
  29.   while(n>F[k]-1)//计算n位于斐波那契数列的位置  
  30.       ++k;  
  31.   
  32.   int  * temp;//将数组a扩展到F[k]-1的长度  
  33.   temp=new int [F[k]-1];  
  34.   memcpy(temp,a,n*sizeof(int));  
  35.   
  36.   for(int i=n;i<F[k]-1;++i)  
  37.      temp[i]=a[n-1];  
  38.     
  39.   while(low<=high)  
  40.   {  
  41.     int mid=low+F[k-1]-1;  
  42.     if(key<temp[mid])  
  43.     {  
  44.       high=mid-1;  
  45.       k-=1;  
  46.     }  
  47.     else if(key>temp[mid])  
  48.     {  
  49.      low=mid+1;  
  50.      k-=2;  
  51.     }  
  52.     else  
  53.     {  
  54.        if(mid<n)  
  55.            return mid; //若相等则说明mid即为查找到的位置  
  56.        else  
  57.            return n-1; //若mid>=n则说明是扩展的数值,返回n-1  
  58.     }  
  59.   }    
  60.   delete [] temp;  
  61.   return -1;  
  62. }  
  63.   
  64. int _tmain(int argc, _TCHAR* argv[])  
  65. {  
  66.     int a[] = {0,16,24,35,47,59,62,73,88,99};  
  67.     int key=100;  
  68.     int index=Fibonacci_Search(a,sizeof(a)/sizeof(int),key);  
  69.     cout<<key<<" is located at:"<<index;  
  70.     system("PAUSE");  
  71.     return 0;  
  72. }  

斐波那契查找的核心是:
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]-1mid把数组分成了左右两部分,左边的长度为:F[k-1] -1那么右边的长度就为(数组长-左边的长度-1),即:(F[k]-1F[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),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。

     下面来看下斐波那契查找代码实现:

[cpp] view plain copy
  1. /*斐波那契查找法,前提是线性表必须有序,时间复杂度是O(logn)*/  
  2. #include<iostream>  
  3. const int MAXSIZE = 20;  
  4. int Fibonacci_Search(int *a, int n, int key);  
  5. /*用非递归法构造一个斐波那契数组*/  
  6. void Fibonacci(int *f)  
  7. {  
  8.     f[0] = 1;  
  9.     f[1] = 1;  
  10.     for(int i=2; i<MAXSIZE; i++)  
  11.     {  
  12.         f[i] = f[i-1] + f[i-2];  
  13.     }  
  14. }  
  15.   
  16. int main()  
  17. {     
  18.     int array[] = {0,16,24,35,47,59,62,73,88,99};  
  19.     int number = Fibonacci_Search(array, sizeof(array)/sizeof(int), 73);  
  20.     std::cout<<"位置是:array["<<number<<"]\n";  
  21.     return 0;  
  22. }  
  23.   
  24. /*定义斐波那契查找法*/  
  25. int Fibonacci_Search(int *a, int n, int key)  
  26. {  
  27.     int low, high, mid, i, k;  
  28.     int F[MAXSIZE];  
  29.         Fibonacci(F); //构造一个斐波那契数组F  
  30.     low = 1;   //最低下标记录为首位  
  31.     high = n;  //最高下标记录为末位  
  32.     k = 0;  
  33.     while(n > F[k]-1)  //计算n位于斐波那契数列的位置  
  34.     {  
  35.          k++;  
  36.     }  
  37.   
  38.     for(i=n; i<F[k]-1; i++)  //将a的元素扩展到(某斐波那契数 - 1),即F[k]-1  
  39.     {  
  40.          a[i] = a[n];  
  41.     }  
  42.   
  43.     while(low <= high)  
  44.     {  
  45.         mid = low + F[k-1] - 1;   //计算当前分割的下标  
  46.         if(key < a[mid])  
  47.         {  
  48.             high = mid - 1;  
  49.             k -= 1;  
  50.         }  
  51.         else if(key > a[mid])  
  52.         {  
  53.             low = mid + 1;  
  54.             k -= 2;  
  55.         }  
  56.         else  
  57.         {  
  58.             if(mid <= n)  
  59.                 return mid;   //若相当则说明mid即为查找到的位置  
  60.             else  
  61.                 return n;     //若mid>n则说明是扩展的数值,返回n  
  62.         }  
  63.     }  
  64.     return -1;  
  65. }  


0 0