1、原理
类似于二分法查找,它们的不同之处在于,斐波那契查找将查找点的对半选择改进为黄金分割点选择,所以又叫做黄金比例查找法;
其中 searchVal代表需要查找的值,fid是斐波那契数列;
2、基础知识
2.1 斐波那契数列
1,1,2,3,5,8,13,21…F(n-1),F(n),其一般表达式为F(n)=F(n-1)+F(n-2),其中F(1)=1,F(2)=1,中文解释即一个数字等于它前面两项之和;
2.2 黄金分割
证明:当n趋近无穷大时,F(N-1)/F(N)=0.618(黄金分割点);
3、算法特点
1、有序列表,同类有二分法、插值法;
斐波那契数列的最大位置pos,先是由列表的长度len决定的,然后再角色互换来决定列表长度len;pos取值规则为fib[pos]>=len中的最小值,当列表的长度len小于fib[pos]的取值时,需要重建列表长度为fib[pos],并使用列表的原先最高位值a[len-1]填补增加的位置[len,fib[pos]-1];
2、黄金分割;
3、避免除法运算,在计算机中除法运算的占用资源比加减法高;
4、时间复杂度O(logn)
4、 图解
5、算法代码:
package xw.zx.algorithm.search;
public class FibSearch {
private static final int FIB_SIZE=100;
private int[] fibArr=new int[FIB_SIZE];
public FibSearch() throws RuntimeException{
super();
this.fibArr = generatorFibArray(FIB_SIZE);
}
/**
* 构造斐波那契数列
* @param n
* @return
*/
private int[] generatorFibArray(int n) {
if(n<1) {
throw new RuntimeException("n值必须大于等于1");
}
int[] fibArr=new int[n];
fibArr[0]=1;
fibArr[1]=1;
for(int i=2;i<n;i++) {
fibArr[i]=fibArr[i-1]+fibArr[i-2];
}
return fibArr;
}
/**
* 获取比iniArr的长度恰大一些的,斐波那契位置
* @param iniArr
* @return
*/
private int findMaxPos(int[] iniArr) {
int pos=0;
int len=iniArr.length;
try {
while(len>fibArr[pos]-1) {
pos++;
}
} catch (Exception e) {
throw new RuntimeException("斐波那契数列长度不够");
}
return pos;
}
/**
* 重建数组
* @param iniArr
* @param pos
* @return
*/
private int[] rebuildArr(int[] iniArr,int pos) {
int[] arr=new int[fibArr[pos]];
int len=iniArr.length;
System.arraycopy(iniArr, 0, arr, 0, len);
for (int i = len; i <= fibArr[pos]-1; i++) {
arr[i] = iniArr[len-1];
}
return arr;
}
/**
* 斐波那契查找法,
*
* @param arr
* @param searchVal
* @return
*/
public int fibSearch(int[] arr,int searchVal) {
//初始化查询次数、下界low、上界high、数组原始长度len
int count=0,low =0,high=arr.length-1,len=arr.length;
int mid,guess,pos;
//获取斐波那契最大位置;
pos=findMaxPos(arr);
//重建原始数组
arr=rebuildArr(arr,pos);
while(low<=high) {
//low+黄金分割点,之所以减1的原因是数组的开始索引从0开始,而斐波那契数列以1开始;
mid=low+fibArr[pos-1]-1;
guess=arr[mid];
System.out.println("guess:"+guess);
count++;
//找到值
if(guess==searchVal) {
if(mid<len-1) {
System.out.println("下标:"+mid );
}else {
//存在mid超出原始数组长度的情况,因为原始数组扩容过
System.out.println("下标:"+(len-1));
}
return count;
}
//定界
if(guess>searchVal) {
high=mid-1;
/**
* 之所以减1,是因为数组的剩余查找部分[low,high],在下一次迭代中长度变为fib[pos-1],
* 准确地讲,剩余长度应该为fib[pos-1]-1,因为我们将high设置成了mid-1;
*/
pos=pos-1;
}else {
low=mid+1;
/**
* 之所以减2,是因为数组的剩余查找部分[low,high],在下一次迭代中长度变为fib[pos-2],
* 准确地讲,存在剩余长度为fib[pos-2]-1的情况,因为我们将high设置成了mid-1;
*/
pos=pos-2;
}
}
return -1;
}
public static void main(String[] args) {
int cnt;
//原始数组
int[] iniArr= {3,6,8,9,13,16,20,21,25};
//查找值
int searchVal=8;
FibSearch fib=new FibSearch();
//调用斐波那契查找法
cnt=fib.fibSearch(iniArr, searchVal);
System.out.println("查找次数: "+cnt);
}
}
输出结果: