斐波那契查找是一种在有序表中高效查找指定元素的算法,比折半查找要复杂一些,主要复杂在要多做不少准备工作。下面看它的工作流程:
1.计算并保存一个斐波那契序列的数组,方便以后取值。数组名记为f,例如f[1]=1,f[2]=1,f[3]=2,f[4]=3,f[5]=5,f[6]=8,f[7]=13,f[8]=21
2.把有序数组的长度扩充到a.length=f[k]-1,k是满足条件的最小值,比如数组长度为13,那么就把它长度扩充到f[8]-1=20,所有在末尾添加的扩充元素都是原数组最后一个元素的复制品
3.找到mid元素,不断进行二分比较,直到找到目标元素为止,这一步的做法与折半查找一模一样,仅仅是计算mid的公式从(low+high)/2改为low+(f[k-1]-1)。
斐波那契查找的理解难点就一个:为什么需要把数组长度扩充到f[k]-1而不是f[k]或者
f[k+1]?这是为了能正确递归计算mid值,看下图可发现 f[k]-1 = (f[k-1] + f[k-2]) - 1 =
(f[k-1]-1) + 1
+ (f[k-2]-1),中间的1就是我们二分的锚点mid,如果目标在左区,数组长度就缩到(f[k-1]-1),如果在右区,数组长度就缩到
(f[k-2]-1),否则就等于mid完成查找。而(f[k-1]-1)又能拆成(f[k-2]-1)+1+(f[k-3]-1),这样递归分割下去就
能不断的缩小区间直至找到目标。
#include <stdio.h> #define LEN 9 #define MAXSIZE 100 int F[50];//fibonacci数列 //构造fibonacci数列 void FibonacciArray(int * a){ int i; a[0]=1; a[1]=1; for(i=2; i<50; i++){ a[i]=a[i-1]+a[i-2]; } } //进行fibonacci查找,返回0即查找失败,否则返回找到的下标 int FibonacciSearch(int * a,int n,int key){ int k=0,i; int mid,low=1,high=n; while(n>F[k]-1) k++;//确定初始k值 for(i=n+1; i<=F[k]-1; i++ ){ a[i]=a[n]; //补全a[n+1]到a[F[k]-1],使之都==a[n] } //开始查找 while(low<=high){//注意 mid=low+F[k-1]-1; if(a[mid]>key){//如果中间值比要查找的值大 high=mid-1; k=k-1; }else if(a[mid]<key){ low=mid+1; k=k-2; }else{//a[mid]==key if(mid<=n) return mid; else if(mid>n)//说明找到的下标在原来填补的位置上,是和a[n]相同的,所以返回n return n; } } return 0;//返回0,即key不在数组a中 } int main(){ int a[LEN+1]={0,8,23,45,78,89,25,68,47,59};//设首位为哨兵 int arr[MAXSIZE]={0};//设一个足够大的数组,代入函数计算 for(int i=0; i<LEN+1; i++){ arr[i]=a[i]; } int result; int key=59; FibonacciArray(F);//返回[1,1,2,3,5,...] //printf("%d",F[7]); result=FibonacciSearch(arr,LEN,key); if(result!=0){ printf("%d存在数组a中,等于a[%d]\n",key,result); }else printf("数组a中不存在%d\n",key); return 0; }
结果:
之前写的时候返了一个错误,while(low<=high)小于等于写成了<;
参考:
[1]大话数据结构,p.303-304
[2]http://blog.csdn.net/lzdidiv/article/details/59784694
时间: 2024-11-20 11:42:12