斐波那契查找其实挺不容易理解的,个人感觉。其实分析完了也就那回事。
黄金分割搜索(又 斐波那契搜索)是一种通过不断缩小单峰函数的最值的已知范围,从而找到最值的方法。它的名称源于这个算法保持了间距具有黄金分割特性的三个点。这个算法与斐波那契搜索和二分查找关系紧密。
因为斐波那契数列符合黄金分割,所以,黄金分割搜索,是借助了斐波那契数列实现了搜索间隔。所以也可以称之为 斐波那契搜索。
下面,开始对斐波那契搜索中的一些要点进行分析。
1. 分割点选取公式
mid = left + F(n-1) -1 (F 是斐波那契数列)
2. 公式在图形中的体现
要想了解公式,首先做一个约定,我们约定:
如果待搜索数组长度不符合一个斐波那契数,就用最后一个数据将数组填充到一个刚好大于数组长度的斐波那契数。例如 13 < dataset.length = 18 < 21 我们就将数组扩容至存储21个元素,即 dataset.length = 21 ,扩容后的多余空间用最后一个数据 17 填充
待搜索数据集的数据和下标相同。
数据集 dataset 数据和下标对应关系:
数据:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
下标:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
使之长度符合斐波那契数
数据:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 17, 17
下标:0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
搜索所用的斐波那契数列为:
斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55
斐波数列下标:0, 1, 2, 3, 4, 5, 6, 7, 8, 9
将数据集图形化:
长度21 是斐波那契数 F(7) ,而 F(7) = F(6) + F(5) = 13 + 8
在图形上体现为:
以此类推,继续用斐波那契数列分
以上,就是 dataset 被斐波那契数列拆分的完整过程。
ok 现在来说,F(n-1) - 1
先说,为什么 -1 因为数组的长度 dataset.length 等于 一个斐波那契数,所以,数组下标有效范围是dataset.length - 1 , 斐波那契拆分为了保证下标不越界,就减了 1,其实就是数组下标。
ok 现在,我们模拟一下查询数据。
比如,我们要查询11。
首先,确定 n 值,n 是斐波那契数的下标,我们的数组被扩容成了 dataset.length = 21 = F(7)
所以
n = 7
target = 11
left = 0
mid = left +F(n-1) -1 = 0 + F(6) -1 = 0 + 13 -1 = 12
dataset[mid] = dataset[12] = 12 > target = 12 > 11
如果 target < dataset[mid]
根据上图,我们可以看到,如果 target < dataset[mid] 就是在 mid 分割点左边,
我们就去分割F(n-1),否则,我们就分割 F(n-2),
而这一次,我们的 target 就小于 dataset[mid] ,所以,我们让 n = n-1,再去计算下一个 mid 值
n = n-1 = 6
即 mid = left +F(n-1) -1 = 0 + F(6-1) -1 = 0 + F(5) -1 = 0+8-1 = 7,
target > dataset[7]
这次,是右边,n = n-2 = 6-2 = 4
重新确定 left 值 为 mid + 1 = 8
再计算 mid = left +F(n-1) -1 = 8 + F(4-1) -1 = 8 + F(3) -1 = 8+3-1 = 10
target > dataset[10]
n = n-2 = 4-2 = 2
left = 10+1 = 11
mid = left +F(n-1) -1 = 11 + f(2-1) -1 = 11 + f(1) -1 = 11+1-1 = 11
target == dataset[11]
至此,查结束。我们同时也可以确定,结束循环的条件是 n > 1
下面,代码实现:
public class FibSearch {
public static int search(int[] dataset, int target) {
int n = 0;
int mid = 0;
int left = 0;
int[] fib = new int[20];
fib[0] = 1;
fib[1] = 1;
for (int i = 2; i < 20; i++) {
fib[i] = fib[i - 1] + fib[i - 2];
if (dataset.length > fib[i]) {
n = i + 1;
}
}
// dataset 扩容
int[] temp = new int[fib[n]];
for (int i = 0;i<temp.length;i++){
if (i>=dataset.length){
temp[i] = dataset[dataset.length-1];
}else {
temp[i] = dataset[i];
}
}
while (n > 1) {
mid = left + fib[n - 1] - 1;
if (target < temp[mid]) {
n = n - 1;
}
if (target > temp[mid]) {
left = mid + 1;
n = n - 2;
}
if (temp[mid] == target){
if (mid >= dataset.length){
mid = dataset.length-1;
}
return mid;
}
}
return -1;
}
}
完整分析图