一、基本思路
在介绍斐波那契查找之前,让我们先了解几个基本的概念:
- 黄金分割点:是指把一条线段分成两部分,使其中一部分与全长之比等于另一部分与这部分之比,其比值保留三位小数约为0.618。由于按此比例设计的造型十分美丽,因此成为黄金分割,也称为中外比。
- 斐波那契数列:{1,1,2,3,5,8,13,21,34,55…}即相邻两个数的和等于这两个数后面的数,通过计算我们可以发现,斐波那契数列中的两个相邻数的比例,无限接近黄金分割值0.618。
要注意的是:斐波那契查找和前面的两个查找方式类似,也要求待查找的序列是有序的。
基本思路: 斐波那契查找与二分查找和插值插值相似,只不过改变了划分的节点,不再是中点和插值点,而是位于黄金分割点附近,所以我们需要利用斐波那契数列的特性来找到这个黄金分割点。
- 由斐波那契数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)
- 类似的,每一子段也可以用相同的方式分割
- 但顺序表长度 n 不一定刚好等于 F[k]- 1 ,所以需要将原来的顺序表长度 n 增加至 F[k]- 1 。这里的 k 值只要能使得 F[k]-1恰好少于或等于 n 即可,由以下代码得到,顺序表长度增加后,新增的位置(从 n + 1到 F[k]- 1 位置),都赋值为 n 位置的值即可。
while ( n > f[k]-1)
k++;
简单来说: 就是根据待排序列的个数,利用斐波那契数列找到一个大于等于序列个数的数,那么这个数前面两个数之比就是分割比例。
二、算法分析
斐波那契查找可以使用递归实现,也可以使用循环实现。
时间: 当用递归实现时,根据递归树的高度,可得时间复杂度O(
2
n
2^n
2n),当用循环实现时,时间复杂度位O(n)
空间: 当用递归实现时,空间复杂度O(n),当用循环实现时,空间复杂度位O(n)
算法 | 平均时间 | 最好情形 | 最差情形 | 空间复杂度 | 备注 |
---|---|---|---|---|---|
斐波那契查找 | O(n) | O(n) | O( 2 n 2^n 2n) | O(n) | 待查表是有序表 |
三、代码实现
这里采用循坏来实现:
import java.util.Arrays;
/**
* @author dankejun
* @create 2020-05-03 14:49
*/
public class FibonaciSearch {
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1, 8, 10, 89, 1000, 1234};
System.out.println("下标为:" + fibSearch(arr,89));
}
//非递归得到一个斐波那契数列
public static int[] fib() {
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 2] + f[i - 1];
}
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;
int[] f = fib();
//获取到斐波那契分割数值的下标
while (high > f[k] - 1) {//未找到
k++;
}
int[] temp = Arrays.copyOf(a, f[k]);
for (int i = high+1; i < temp.length; i++) {//将空的位置补齐
temp[i] = a[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;
}
}
测试序列: int arr[] = {1, 8, 10, 89, 1000, 1234};查找是否含有元素89
测试结果:
测试序列: int arr[] = {1, 8, 10, 89, 1000, 1234};查找是否含有元素34
测试结果: