【算法扩展】斐波那契查找算法 - JAVA
一、算法概述
斐波那契查找是一种在有序数组中进行查找的算法,它与二分查找类似,但使用斐波那契数列来确定分割点。算法的核心思想是将查找区间按照黄金分割比例(约0.618)进行划分,而不是像二分查找那样均分为二。这种分割方式在某些场景下可以获得更好的性能。
斐波那契数列基础知识
斐波那契数列定义为:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2) (n≥2)
前几项为:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...
二、时间复杂度
斐波那契查找的时间复杂度为 O(log n),与二分查找相同。空间复杂度为 O(1),只需存储几个变量。
三、查找思路与公式
1. 基本思路
- 找到不小于数组长度n的最小斐波那契数F(k)
- 将数组长度扩展到F(k)-1(必要时填充)
- 使用F(k-1)-1作为分割点,将数组分为两部分:
- 前半部分长度为F(k-1)-1
- 后半部分长度为F(k-2)
2. 分割公式
在斐波那契查找中,分割点的计算公式为:
mid = low + F(k-1) - 1
- F(k-1) 是斐波那契数列中的一项,k是使得F(k)-1不小于数组长度的最小值。
- 在斐波那契查找中,F(k-1)对应黄金分割点左边的长度
- 这个公式反映了黄金分割比例(约为1:0.618)。当数组按照这个比例分割时,大约61.8%的数据会在左边,38.2%的数据会在右边,这就是利用斐波那契数列进行查找的数学基础。
3. 调整斐波那契序列
- 当目标在右半部分时:k = k-2,因为右半部分长度约为F(k-2)
- 当目标在左半部分时:k = k-1,因为左半部分长度约为F(k-1)-1
四、代码示例
public class FibonacciSearch {public static int fibonacciSearch(int[] arr, int key) {int n = arr.length;if (n == 0) return -1;// 构造斐波那契数列int fibMMinus2 = 0; // F(k-2)int fibMMinus1 = 1; // F(k-1)int fibM = fibMMinus1 + fibMMinus2; // F(k)// 找到不小于n的最小斐波那契数while (fibM < n) {fibMMinus2 = fibMMinus1;fibMMinus1 = fibM;fibM = fibMMinus1 + fibMMinus2;}// 定义数组的偏移量int offset = -1;// 循环查找while (fibM > 1) {// 计算比较点的索引int i = Math.min(offset + fibMMinus2, n - 1);// 比较并决定向左还是向右if (arr[i] < key) {// 目标在右半部分,调整斐波那契数列fibM = fibMMinus1;fibMMinus1 = fibMMinus2;fibMMinus2 = fibM - fibMMinus1;offset = i; // 更新偏移量} else if (arr[i] > key) {// 目标在左半部分,调整斐波那契数列fibM = fibMMinus2;fibMMinus1 = fibMMinus1 - fibMMinus2;fibMMinus2 = fibM - fibMMinus1;} else {// 找到目标return i;}}// 检查最后一个元素if (fibMMinus1 == 1 && offset + 1 < n && arr[offset + 1] == key) {return offset + 1;}// 未找到目标return -1;}public static void main(String[] args) {int[] arr = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50};int key = 25;int result = fibonacciSearch(arr, key);if (result != -1) {System.out.println("元素 " + key + " 在数组中的索引位置: " + result);} else {System.out.println("元素 " + key + " 不在数组中");}}
}
五、示例
假设有一个有序数组 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50],查找元素25:
- 数组长度为10,找到不小于10的斐波那契数F(7)=13
- F(6)=8, F(5)=5
- 第一次比较:mid = 0 + F(6) - 1 = 0 + 8 - 1 = 7,arr[7] = 40 > 25
- 目标在左半部分,调整斐波那契数:F(7)→F(5), F(6)→F(5), F(5)→F(4)
- 第二次比较:mid = 0 + F(4) - 1 = 0 + 3 - 1 = 2,arr[2] = 15 < 25
- 目标在右半部分,调整斐波那契数:F(5)→F(4), F(5)→F(3), F(4)→F(2)
- 更新offset = 2
- 第三次比较:mid = 2 + F(2) - 1 = 2 + 1 - 1 = 2,arr[2] = 15 < 25
- 目标在右半部分,调整斐波那契数:F(4)→F(3), F(3)→F(2), F(2)→F(1)
- 更新offset = 2
- 第四次比较:mid = 2 + F(1) - 1 = 2 + 1 - 1 = 2,arr[2] = 15 < 25
- 目标在右半部分,调整斐波那契数:F(3)→F(2), F(2)→F(1), F(1)→F(0)
- 更新offset = 2
- 检查offset+1 = 3,arr[3] = 20 < 25
- 检查offset+2 = 4,arr[4] = 25 = 25,找到目标!
六、适用场景和优缺点
适用场景
- 磁盘等外存查找:减少对前半部分的访问
- 大型有序数组:在某些数据分布下比二分查找更高效
- 减少比较次数的场景:按黄金分割比搜索有时能减少平均比较次数
优点
- 分割更自然:按照黄金分割比例划分区间
- 在某些场景下效率更高:特别是当数据访问成本不均匀时
- 只需加减运算:不需要像二分查找那样进行除法操作
缺点
- 实现较复杂:需要管理斐波那契数列和相关逻辑
- 需要额外处理:当数组长度不是斐波那契数减1时需要特殊处理
- 优势不总是明显:在现代计算机中,优势可能不如理论预期
七、总结
斐波那契查找是一种利用黄金分割原理的查找算法,它按照斐波那契数列的特定比例划分查找区间。虽然与二分查找时间复杂度相同,但在某些特定场景下可能更有效率。这种算法展示了如何利用数学性质优化搜索过程,是算法设计思想的一个很好例证。