您的位置:首页 > 理论基础 > 数据结构算法

一步一步复习数据结构和算法基础-斐波那契查找法

2012-07-23 10:13 232 查看
view plaincopyprint?

/*斐波那契查找法,前提是线性表必须有序,时间复杂度是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
;

}

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;

}

首先要明确:如果一个有序表的元素个数为n,并且n正好是(某个斐波那契数 - 1),即n=F[k]-1时,才能用斐波那契查找法。 如果有序表的元素个n不等于(某个斐波那契数 - 1),即n≠F[k]-1,这时必须要将有序表的元素扩展到大于n的那个斐波那契数 - 1才行,这段代码:

[cpp]view
plaincopyprint?

for (int i = n; i < F[k] - 1; i++)

{

a[i] = a
;

}

便是这个作用。

数组a的长度其实很好估算,比如你定义了有10个元素的有序数组a[20],n=10,那么n就位于8和13,即F[6]和F[7]之间,所以k=7,此时数组a的元素个数要被扩充,为:F[7] - 1 = 12个; 再如你定义了一个b[20],且b有12个元素,即n=12,那么很好办了,n = F[7]-1 = 12, 用不着扩充了; 又或者n=8或9或11,则它一定会被扩充到12; 再如n=13,最后得出n位于13和21,即F[7]和F[8]之间,此时k=8,那么F[8]-1 = 20,数组a就要有20个元素了。
所以,n = x(13<=x<=20)时,最后都要被扩充到20;类推,如果n=25呢,则数组a的元素个数肯定要被扩充到 34 - 1 = 33个(25位于21和34,即F[8]和F[9]之间,此时k=9,F[9]-1 = 33),所以,n = x(21<=x<=33)时,最后都要被扩充到33。也就是说,最后数组的元素个数一定是(某个斐波那契数 - 1),这就是一开始说的n与F[k]-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) = 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]范围内查找。

关于斐波那契查找, 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当众的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂度也为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),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。

博客地址:http://blog.csdn.net/zpluw/article/details/7757331
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息