package com.hejiale.arithmetic.search;
import java.util.Arrays;
/*
斐波那契查找算法也是针对二分查找的,只不过类似于插值查找,我们又是修改的求mid的公式
1. 首先,说明白斐波那契数列,斐波那契数列的特点是,当下标大于等与2的时候,f[k]=f[k-1]+f[k-2],首先给一个
斐波那契数列方便理解
{1 1 2 3 5 8 13 21 34 55 89 144 233}
我们可以发现:2=1+1 8=5+3 21=13+8 233=144+59
那么我们就可以联想到一点,如果我们的数组,长度等于斐波那契数列中的值,那么一定可以将其拆分成两部分
比如数组长度为8,那么我们可以通过斐波那契数组,将其分为两部分,长度分别为3,和5,既然我们可以将数组分
成两部分,就可以联想到我们之前的二分查找和插值查找,因为他们归根到底,也是把数组分成两部分递归查找的
2. 但是实际开发中,我们的数组的长度,不一定刚刚好是斐波那契数列中的值,那么这个时候,我们应该对原始数组
进行扩充,直到长度等于斐波那契数列中的值,然后将数组拆分即可
3. 接下来,我们应该确定的就是mid的取值
mid = low + f[k-1]-1
为什么呢?
1. 首先,low是左边界的值,不能是0,因为涉及到递归,我们可能递归的是左部分,这个时候用0就是错误的,所以
应该是low而不是0
2. 其次,f[k-1]是因为,我们的f[k]=f[k-1]+f[k-2],所以f[k-1]其实就是整个数组的分割点,比如8=3+5
那么3/5就是分割点,我们可以让f[k-1]作为分割点,也可以让f[k-2]作为分割点
3. 至于-1,因为我们f[k]涉及的是长度,要换成下标,就应该-1
4. 那么我们的开发步骤就清晰了
1. 准备一个合适长度的斐波那契数列
2. 如果数组的长度,不是斐波那契数列中的值,我们就扩充数组长度,直到找到对应的斐波那契数列的值,扩充的方法
就是对数组的最后一个元素的复制
3. 数组长度也准备好后,接下来就是核心代码,准备一个while循环(low <= high)
1. 如果key < temp[mid],说明我们应该在左部分继续查找
high = mid-1;
k--;
k--这里不好理解,首先,我们的mid=low+f[k-1]-1;也就是说,如果数组长度为f[5]8,那么左半部分是5个元素
右半部分是3个元素,那么左半部分是五个,所以我们针对的就是这五个,f[4] =5,所以是k--
2. 如果key > temp[mid],说明我们应该在右部分继续查找
low = mid+1;
k-=2;
那么这里我们还是假设数组长度为8,那么我们在右半部分查找,右半部分就应该有3个元素,f[3]=3,所以说是k-=2
3. 如果key = temp[mid],说明查找成功,返回mid即可
5. 总结:斐波那契查找的好处是,算法中mid的取值不涉及到/*,只涉及加减,效率会得到一定的提升
*/
public class FibonacciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1, 8, 10, 89, 1000, 1234};
/*int[] fib = fib();
for (int i : fib) {
System.out.print(i+" ");
}*/
System.out.println(fibSearch(arr, 12));
}
//因为后面的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 查找的关键字
* @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 + 1 > f[k]) {
k++;
}
/*
f[k]对应的值不一定和数组大小相同,有可能大于数组长度,如果大于,我们就要填充数组,直到长度相等,填充的就是
数组的最后一个元素
*/
int[] temp = Arrays.copyOf(a, f[k]);
for (int i = high + 1; i < temp.length; i++) {
temp[i] = a[high];
}
//使用while来循环处理,找到key,当low>high的时候跳出循环
while (low <= high) {
mid = low + f[k - 1] - 1;
if (key < temp[mid]) {//说明应该继续向数组的前部分查找
high = mid - 1;
k--;
} else if (key > temp[mid]) {//说明应该向数组的后半部分查找
low = mid + 1;
k -= 2;
} else { // 找到了
// 返回下标,需要确定返回的下标,因为我们之前,对数组进行了扩充
if (mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}
}
11-20
5137