元素的个数实质为F(n)个,前半部分元素个数为F(n-1)个,后半部分元素个数为F(n-2)个,下标从0开始,所以长度为F(n)-1,mid=low+F(n-1)-1,这里的F(n-1)是对应的斐波那契数列中的那个值
什么是斐波那契查找
斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。
斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为F[n](如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
斐波那契查找的时间复杂度还是O(log 2 n ),但是 与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是还是得视具体情况而定。
对于斐波那契数列:1、1、2、3、5、8、13、21、34、55、89……(也可以从0开始),前后两个数字的比值随着数列的增加,越来越接近黄金比值0.618。比如这里的89,把它想象成整个有序表的元素个数,而89是由前面的两个斐波那契数34和55相加之后的和,也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段,那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618,假如要查找的元素在前半段,那么继续按照斐波那契数列来看,55 = 34 + 21,所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段,继续查找,如此反复,直到查找成功或失败,这样就把斐波那契数列应用到查找算法中了。
从图中可以看出,当有序表的元素个数不是斐波那契数列中的某个数字时,需要把有序表的元素个数长度补齐,让它成为斐波那契数列中的一个数值,当然把原有序表截断肯定是不可能的,不然还怎么查找。然后图中标识每次取斐波那契数列中的某个值时(F[k]),都会进行-1操作,这是因为有序表数组位序从0开始的,纯粹是为了迎合位序从0开始。所以用迭代实现斐波那契查找算法如下:
#include <stdio.h>
2
3 #define FIB_MAXSIZE 100
4
5 /**
6 * 生成斐波那契数列
7 * @param fib:指向存储斐波那契数列的数组的指针
8 * @param size:斐波那契数列长度
9 */
10 void ProduceFib(int *fib, int size)
11 {
12 int i;
13
14 fib[0] = 1;
15 fib[1] = 1;
16
17 for (i = 2; i < size; i++)
18 {
19 fib[i] = fib[i - 1] + fib[i - 2];
20 }
21 }
22
23 /**
24 * 斐波那契查找,查找成功返回位序,否则返回-1
25 * @param data:有序表数组
26 * @param length:有序表元素个数
27 * @param searchValue:待查找关键字
28 */
29 int FibonacciSearch(int *data, int length, int searchValue)
30 {
31 int low, high, mid, k, i, fib[FIB_MAXSIZE];
32
33 low = 0;
34 high = length - 1;
35
36 ProduceFib(fib, FIB_MAXSIZE);
37
38 k = 0;
39 // 找到有序表元素个数在斐波那契数列中最接近的最大数列值
40 while (high > fib[k] - 1)
41 {
42 k++;
43 }
44
45 // 补齐有序表
46 for (i = length; i <= fib[k] - 1; i++)
47 {
48 data[i] = data[high];
49 }
50
51 while (low <= high)
52 {
53 mid = low + fib[k - 1] - 1; // 根据斐波那契数列进行黄金分割
54
55 if (data[mid] == searchValue)
56 {
57 if (mid <= length - 1)
58 {
59 return mid;
60 }
61 else
62 {
63 // 说明查找得到的数据元素是补全值
64 return length - 1;
65 }
66 }
67
68 if (data[mid] > searchValue)
69 {
70 high = mid - 1;
71 k = k - 1;
72 }
73
74 if (data[mid] < searchValue)
75 {
76 low = mid + 1;
77 k = k - 2;
78 }
79 }
80
81 return -1;
82 }
83
84 int main()
85 {
86 int data[] = {1,3,5,7,9,11,13,15,17,19,21};
87
88 int index = FibonacciSearch(data, 11, 19);
89 printf("%d
", index);
90
91 return 0;
92 }
斐波那契查找的时间复杂度还是O(log2n),但是与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是还是得视具体情况而定