斐波那契(黄金分割法)原理:
斐波那契查找原理与前两种相似,仅仅改变了中间节点(mid)的位置,mid不再是中间值或插值得到,而是位于黄金分割点附近,即mid=low+F(k-1)-1
(F代表斐波那契数列),如下图所示
对F(k-1)-1的理解
1)由斐波那契数列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位置的值即可。
斐波那契查找应用案例:
请对一个有序数组进行斐波那契查找 {1,8, 10, 89, 1000, 1234} ,输入一个数看看该数组是否存在此数,并且求出下标,如果没有就提示"没有这个数"
代码实现
package com.atguigu.search;
import java.util.ArrayList;
import java.util.Arrays;
public class FibonaqiSearch {
public static int maxSize=20;
public static void main(String[] args) {
int arr[]={1,8,10,89,1000,1234};
System.out.println(fibSearch(arr,1000));
}
// 因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取一个斐波那契数列
// 非递归方法得到一个斐波那契数列
public static int[] fib(){
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;
}
// 编写斐波那契查找算法
// 使用非递归的方式编写算法
/**
*
* @param a数组
* @param key key 我们需要查找的关键码值()
* @return 返回对应的下标,如果没有-1
*/
public static int fibSearch(int[] a,int key){
int low=0;
int high=a.length-1;
int k=0;//表示斐波那契分割数值的下标
int mid=0;//存放mid值
int f[]=fib();//获取到斐波那契分割数值的下标
while(high>f[k]-1){
k++;
}
//因为f[k]值可能大于a的长度,因此我们需要使用Arrays类,构造一个新的数组,并指向a[]
// 不足的部分会使用0填充
int[] temp= Arrays.copyOf(a,f[k]);
// 实际上需求使用a数组最后的数填充temp
// 举例:
// temp={1,8,10,89,1000,1234,1234,1234,1234}
for(int i=high+1;i<temp.length;i++){
temp[i]=a[high];
}
// 使用while循环来处理,找到我们的数key
while(low<=high){//只要这个条件满足就可以找
mid=low+f[k-1]-1;
if(key<temp[mid]){//我们应该继续向数组的前面查找(左边)
high=mid-1;
// 为什么k--;
// 说明
// 1.全部元素=前面的元素+后面的元素
// 2.f[k]=f[k-1]+f[k-2]
// 因为前面有f[k-1]个元素,所以可以继续拆分f[k-1]=f[k-2]+f[k-3]
// 即在f[k-1]的前面继续查找k--
// 即下次循环mid=f[k-1-1]-1
k--;
} else if (key>temp[mid]) {//我们应该继续向数组的后面查找(右边)
low=mid+1;
// 为什么是k=k-2
// 说明
// 1.全部元素=前面的元素+后面元素
// 2.f[k]=f[k-1]+f[k-2]
// 3.因为后面我们有F[k-2]
k=k-2;
}else{
if(mid<=high){
return mid;
}else{
return high;
}
}
}
return -1;
}
}