一、算法思路
1. 前置知识:斐波那契数列
\(F0=0,\ F1=1,\ Fn=Fn-1+Fn-2\) {0,1,1,2,3,5,8,13,21...}
2. 核心思想
二分查找:使用 除法 分割区间 mid = (left+right)/2 插值查找:使用 比例公式 斐波那契查找:利用 ** 斐波那契黄金分割比例(0.618)** 分割区间 公式: \(mid = left + Fk-1 - 1\)
把数组长度扩充至 斐波那契数 F k-1。 区间划分逻辑: 总长度 \(Fk-1\) 分为两部分:
- 左段长度:\(Fk-1-1\)
- 右段长度:\(Fk-2-1\)
优势:全程只有加减法,没有乘除运算,早期硬件环境效率更高;现代工程很少使用,面试常考原理。
3. 条件限制
- 数组升序有序
- 需要将原数组扩容到长度 = \(Fk-1\),不足的位置用数组最后一个元素填充
- 不断缩小 k 值,直到找到目标
4. 查找流程
- 构建斐波那契数列,找到最小 k,满足 \(Fk-1 \ge arr.length\)
- 创建临时数组 temp,扩容至 \(Fk-1\),空位填充数组末尾最大值
- 初始化
left=0, right=arr.length-1 - 循环条件
left <= right && k>0- 计算
mid = left + F[k-1] - 1 - 三种判断: ①
temp[mid] == target:- 如果
mid < arr.length,返回 mid;否则是扩容填充位置,返回arr.length-1②temp[mid] > target:目标在左侧,k = k - 1③temp[mid] < target:目标在右侧,left = mid + 1,k = k - 2
- 如果
- 计算
- 未找到返回 -1
原理记忆:左区间占 F k-1-1 → k-1;右区间占 F k-2-1 → k-2
二、完整 Java 代码(迭代版,面试标准写法)
java
运行
public class FibonacciSearch {
/**
* 斐波那契查找
* @param arr 升序有序数组
* @param target 查找目标
* @return 索引,找不到返回-1
*/
public static int fibonacciSearch(int[] arr, int target) {
if (arr == null || arr.length == 0) {
return -1;
}
int left = 0;
int right = arr.length - 1;
// 斐波那契数组,预先定义
int[] F = new int[40];
F[0] = 0;
F[1] = 1;
// 生成斐波那契数列
for (int i = 2; i < F.length; i++) {
F[i] = F[i - 1] + F[i - 2];
}
// 找到最小的k,满足 F[k]-1 >= arr.length
int k = 1;
while (F[k] - 1 < arr.length) {
k++;
}
// 扩容临时数组,长度 F[k]-1,空缺位置填充数组最后一个元素
int[] temp = new int[F[k] - 1];
System.arraycopy(arr, 0, temp, 0, arr.length);
for (int i = arr.length; i < temp.length; i++) {
temp[i] = arr[right];
}
// 开始查找
while (left <= right && k > 0) {
int mid = left + F[k - 1] - 1;
if (temp[mid] > target) {
// 目标在左区间 [left,mid-1],长度 F[k-1]-1
k = k - 1;
} else if (temp[mid] < target) {
// 目标在右区间 [mid+1,right],长度 F[k-2]-1
left = mid + 1;
k = k - 2;
} else {
// 找到目标
if (mid <= right) {
return mid;
} else {
// mid落在扩容填充区域,真实位置是数组最后一位
return right;
}
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1, 5, 8, 11, 16, 22, 30};
int target1 = 11;
int target2 = 100;
System.out.println(target1 + " 的索引:" + fibonacciSearch(arr, target1));
System.out.println(target2 + " 的索引:" + fibonacciSearch(arr, target2));
}
}
三、三大有序查找算法面试对比总结
表格
| 算法 | mid 计算公式 | 要求 | 优点 | 缺点 |
|---|---|---|---|---|
| 二分查找 | mid = left+(right-left)/2 | 有序 | 稳定,适用所有有序数组 | 需要除法运算 |
| 插值查找 | \(mid=left+\frac{target-arrleft}{arrright-arrleft}(right-left)\) | 有序 + 数据均匀分布 | 均匀数据下平均速度更快 | 不均匀时退化 O (n),存在除零风险 |
| 斐波那契查找 | mid = left + Fk-1-1 | 有序 | 仅加减法,无乘除,早期硬件友好 | 需要扩容、生成斐波那契数组,现代极少使用 |
面试高频问答
- 斐波那契查找为什么不用除法? 答:依靠黄金分割比例拆分区间,只做加减,在计算机底层加减法运算速度优于乘除。
- 为什么扩容数组并且填充末尾元素? 答:保证数组长度等于 F k-1,严格满足斐波那契区间分割模型;填充最大值防止扩容区域干扰比较。
- 实际开发用哪个? 日常开发优先二分查找;插值、斐波那契更多属于笔试面试考点,工程几乎不用。