/**
* 斐波那契查找,就是利用斐波那契数来构造数组,寻找黄金分割点的数字和待查找数字比较(后面的操作类似二分查找)
*
* 斐波那契数组的特点:
* 1.前两个数外,后面的数都位前两位数的和
* 2.前一个数与后一个数的比例接近0.618,也就是黄金分割率
*
* 1.构造长度为斐波那契数的数组
* 1.1
* 二十位斐波那契数组:F[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
* 如果原数组长度为10,那么构造后符合条件的数组长度最少为13.
* 1.2
* 新构造的数组超过原数组的位数默认为0,需要全部补为原数组的最后一个数
*
* 2计算mid值,按照斐波那契数字的规律
* 新数组newarr可以分为F[k-1]和F[k-2]两部分,k为新数组长度,F为斐波那契数组,F[k-1]/F[k-2]接近0.618
* 新数组的索引范围为0--(k-1) 头索引为0 ,尾索引为newarr.length-1
* mid=zuo + F[k-1]-1
*
* 对 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=zuo+F(k-1)-1
* 说明:数组内每个元素都占一个索引
*
* 3.比较newarr[mid]与待查找值的大小
* 定义两个指针l和r,初始值为newarr数组的最大最小索引,作为循环退出条件
* 如果l>r,说明把数组按各个元素切分都没有找到,就表示值不存在,退出循环
* 循环:
* 如果待查找值比newarr[mid]大,表示待查数在右边
* l=mid+1;
* 此时k的值-1,每次拆分后数组的长度对应斐波那契数组元素,其实这里还与计算公式有关
* 例如13 分为5 ,8
* 8 分为5 ,3
* 如果待查找值比newarr[mid]小,表示待查数在左边
* r=mid-1
* 此时k的值-2,每次拆分后数组的长度对应斐波那契数组元素,
* 例如13 分为5 ,8
* * 5 分为3 ,2
*
*4.返回值
*
* 假如mid值落在了原来的数组扩容后的索引上
* 就要返回arr.length-1 因为扩容后多余的位置都是用arr[arr.length-1]填充的
* 假如mid值没有超过arr.length-1,表示mid值没有落在了原来的数组扩容后的索引上
* 就直接返回mid,此时mid索引的数字就是要查找的数
*
*/
/*
关键点:1.构造斐波那契数列
2.mid值的计算
3.递归(循环)结束条件,k值变化情况
4.返回值是否落入扩容后多余索引上
*/
public class 斐波那契查找 {
public static final int max=20;
public static void main(String[] args) {
int[] arr={4,6,7,9,13,16,21,30,42,76};
System.out.println(FibonacciSearch(arr, 0, arr.length - 1,17));
System.out.println(Arrays.toString(fei()));
}
public static int[] fei(){
int[] Farr=new int[max];
Farr[0]=Farr[1]=1;
for (int i = 2; i < Farr.length; i++) {
Farr[i]=Farr[i-1]+Farr[i-2];
}
return Farr;
}
public static int FibonacciSearch(int[] arr,int left ,int right,int element){
int zuo=left;
int you=right;
int k=0;
int mid;
int[] farr=fei();
int[] newArr=null;
while (arr.length>farr[k]){
k++;
}
if (farr[k]>=arr.length){
newArr = Arrays.copyOf(arr, farr[k]);
//补齐多余位置的数
for (int i=0;i<farr[k]-arr.length;i++){
newArr[arr.length+i]=arr[arr.length-1];
}
}
//此时长度为斐波那契数的数组终于构造完成,可以开始计算mid了
while(zuo<=you){
mid=zuo+farr[k-1]-1;
//向右寻找
if (element>newArr[mid]){
zuo=mid+1;
k-=2;
}
//向左寻找
else if (element<newArr[mid]){
you=mid-1;
k-=1;
}else {
//返回待查找数字的索引
if (mid>right){
return arr.length-1;
}else {
return mid;
}
}
}
return -1;
}
}