斐波拉契法(黄金分割法)查找算法
思想:
我们首先对一个数组长度映射到斐波拉契数组上,找到对应的k值(斐波拉契数组某个值的索引),并把这个k值的元素作为temp数组的长度,将原始数组的数据复制到temp数组上,如果数据量不够,那么将原始数据的最后一位作为填充,填充满整个temp数组。
和折半查找一样的,需要找到一个mid,mid将分割出左右两部分,而通过斐波拉契数组的规律f[k]=f[k-1] + f[k-2],可以知道f[k-1] + f[k-2]分别为右左两部分的长度值,通过判断待查找值在mid的左还是右边,再使用对应k值,重新求得mid,就可以和折半一样最后找到待查找数的位置。
package com.search;
import java.util.Arrays;
public class FibonacciSearch {
public static void main(String[] args) {
int[] arr = {0, 8, 10, 89, 1000, 1234, 12345, 123456, 1234567};
int i = fibSearch(arr, 1234567);
System.out.println(i);
}
public static int[] fib(int maxSize) {
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
public static int fibSearch(int[] a, int key) {
int low = 0;
int high = a.length - 1;
// 当前数组长度在斐波拉契数组中的下标
int k = 0;
int mid = 0;
int[] f = fib(20);
// 获取当前值在斐波拉契数组中的下标
// 也可以使用high > f[k]-1
// 注意:high+1就是数组的长度,也就是说必须在斐波拉契数列找到一个大于或者等于数组长度的值
while (high + 1 > f[k]) {
k++;
}
// 斐波拉契数组中求到的值,实际上就是数组的长度,我们需要将这个长度拿来创建一个新的数组,新数组的长度就是这个值f[k]
int[] temp = Arrays.copyOf(a, f[k]);
// 填充temp数组
for (int i = high + 1; i < temp.length; i++) {
temp[i] = a[high];
}
while (low <= high) {
// 这个点为黄金分割点,每次循环都是求那一段的黄金分割点
// 注意这个k-1,因为每次循环实际上就是已经知道了目标数在上一次分割点的哪一段了,左段或者右段,f[k]就是上一次的mid
// 而如果在右段,那么k-2就可以找到上一次分段中
// f[k-2]就是右端所在的范围,然后我们再对这个范围分割,所以又要-1,就可以拿到这一次分割的左段了
mid = low + f[k - 1] - 1;
// 值在黄金分割点的左边
// 由于每一段都是由左右两段构成,而黄金分割点k则是由k-1和k-2构成
// 我们将k-1的值视为左段,k-2的值视为右段,那么两端之和就是mid,也是k
if (key < temp[mid]) {
high = mid - 1;
k--;
} else if (key > temp[mid]) { // 值在数组的右边
low = mid + 1;
k -= 2;
} else {
// 因为k点是相对于temp数组的
// 需要比较mid和high的大小,因为temp数组是被扩充过的,而mid描述的是temp数组的分割点
// high的最大值又是原数组的,因此我们选择小的返回
return Math.min(mid, high);
// if (mid <= high) {
// return mid;
// } else {
// return high;
// }
}
}
return -1;
}
}