斐波那契(黄金分割法)查找算法:
斐波那契(黄金分割法)原理:**斐波那契查找**原理与前两种相似,仅仅改变了中间结点(mid)的位置,mid不再是中间或插值位于黄金割点附近,即==**mid=low+F(k-1)-1**==
(F代表斐波那契数列),如下图所示
对F(k-1)-1的理解:
1)由斐波那契数列 F[k]=F[k-1]+F[k-2] 的性质,可以得到 (F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1 。该式说明:只要顺序表的长度为F[k]-1,则可以将该表分成长度为F[k-1]-1和F[k-2]-1的两段,即如上图所示。从而中间位置为mid=low+F(k-1)-1
2)类似的,每一子段也可以用相同的方式分割
3)但顺序表长度n不一定刚好等于F[k]-1,所以需要将原来的顺序表长度n增加至F[k]-1。这里的k值只要能使得F[k]-1恰好大于或等于n即可,由以下代码得到,顺序表长度增加后,新增的位置(从n+1到F[k]-1位置),都赋为n位置的值即可。
当我们在mid左边进行查找时:
这时f[k-1]就相当于F[k]了
所以此时的斐波那契公式就变成了 F[k-1] = F[(K-1)-1]+F[(K-1)-2]即 F[k-1] = F[K-2]+F[K-3]
如下图:如果向右边查找也是也一样的拆分道理
代码:
package com.lzh.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 斐波那契查找算法
* 也是要求数组是有序的
*/
public class FibonacciSearch {
//定义一个斐波那契数列的长度
private static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1,8, 10, 89, 1000, 1234,1235,1236};
int index = fibSearch(arr,1235);
System.out.println("要查找的数的下标为:"+index);
}
//用非递归的方法得到一个斐波那契数列
public static int[] fib(){
//创建一个用来存放斐波那契数列的数组
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
//斐波那契公式
// f[k] = f[k-1] + f[k-2];
for (int i = 2;i < maxSize; i++){
f[i] = f[i-1] + f[i-2];
}
return f;
}
/**
* 斐波那契查找
* @param arr 要查找的数组
* @param key 要查找的数值
* @return 返回要查找的数的下标
*/
public static int fibSearch(int[] arr,int key){
//定义各个元素
int low = 0;//数组最左边的索引
int high = arr.length - 1;
int mid = 0;//黄金分割点
int k = 0;//斐波那契数列的下标
int f[] = fib();//得到斐波那契数列
//会有这么一种情况,当要查找的数组的长度不够时,是无法用斐波那契进行查找的,所以我们这里判断数组的长度是否小于斐波那契数列中的元素
//只有当f[k]-1的值大于或等于数组最大下标的时候,我们得到一个斐波那契数列中的值,然后对比数组的长度,如果数组的长度比这个斐波那契数列中的值小,我们就要对数组进行扩容。
//即得到k的值
while(arr.length > f[k] - 1){
k++;
}
//对arr数组进行扩容
//f[k] 为斐波那契数列中的值,后面扩充的位置用0补充
int[] temp = Arrays.copyOf(arr,f[k]);
//但实际上我们要用原数组的最后一个数,进行填充
//int i = high + 1 意思是从原数组最后一位的下一位开始填充
for (int i = high+1; i < temp.length;i++){
temp[i] = arr[high];//让扩容的位置用数组最后一位数值填充
}
//开始使用斐波那契进行查找数值
while(low <= high){//左边的索引小于右边的索引就一直循环
mid = low + f[k-1] -1;//用斐波那契数列中的值+low-1得到temp数组的黄金分割点
if (key < temp[mid]){ //要查找的值小于temp[mid]的值的话就往左边进行查找
high = mid - 1;//让mid左边的值当成最右边的索引
/**
* 斐波那契数列公式 F[K] = F[K - 1] +F[K - 2]
* 这里的k--是因为我们向左进行查找了,这时f[k-1]就相当于F[k]了
* 所以此时的斐波那契公式就变成了 F[k-1] = F[(K-1)-1]+F[(K-1)-2]即 F[k-1] = F[K-2]+F[K-3]
* 如果下一轮还是往左进行查找,就继续这样拆分
*/
k--;
}else if (key > temp[mid]){ //要查找的值小于temp[mid]的值的话就往右边进行查找
low = mid + 1;//让mid右边的第一个值当成最左边的索引
/**
* 斐波那契数列公式 F[K] = F[K - 1] +F[K - 2]
* 这里的k -= 2是因为我们向右进行查找了,这时f[k-2]就相当于F[k]了
* 所以此时的斐波那契公式就变成了 F[k-2] = F[(K-2)-1]+F[(K-2)-2]即 F[k-2] = F[K-3]+F[K-4]
* 如果下一轮还是往右进行查找,就继续这样拆分
*/
k -= 2;
}else{
//如果都不满足,就代表找到了这个数的下标
if (mid <= high){//如果mid<=high就代表我们的mid是要查找的下标,所以把mid返回出去
return mid;
}else{
return high;//否则的话high是我们要查找的下标
}
}
}
//如果循环查找之后没找到,就代表这个数组中没有我们要查找的数,所以我们这里返回-1
return -1;
}
}