斐波那契查找的生动形象解释——“用斐波那契数列来分割蛋糕”
想象你和朋友有一条长长的蛋糕,你们想找出蛋糕中某个特别的点(比如夹心的位置)。蛋糕上有很多均匀排列的标记,你们要快速找到目标标记。
传统的二分查找
- 你们先把蛋糕从中间切开,
- 看目标在左边还是右边,
- 然后继续对半切,
- 一次次折半缩小范围。
斐波那契查找的聪明切法
- 你们用斐波那契数列来决定切蛋糕的位置,
- 斐波那契数列是这样的数列:1, 1, 2, 3, 5, 8, 13, 21, …
- 你们先找一个斐波那契数,刚好比蛋糕长度稍大,
- 然后用这个数来决定切蛋糕的位置,
- 这样切的位置不是简单的中间,而是根据斐波那契数列比例来切,
- 这样能更灵活地缩小查找范围。
具体比喻
假设蛋糕长度是21块,斐波那契数列中21是一个数。
- 你先用第一个斐波那契数13来切蛋糕,
- 看目标在13块之前还是之后,
- 如果目标在前面,你用斐波那契数8继续切,
- 如果目标在后面,你用斐波那契数5继续切,
- 依次用斐波那契数列的数来缩小范围。
为什么用斐波那契数列?
- 斐波那契数列的比例接近黄金分割比例(约0.618),
- 这种比例切分能让查找过程更均匀,减少不必要的比较,
- 适合在某些情况下比二分查找更高效。
斐波那契查找的优点
- 不需要除法运算,只有加减法,适合计算代价高的环境。
- 利用斐波那契数列的性质,减少查找次数。
- 对于某些硬件或存储结构,访问代价不均匀时表现更好。
小结
斐波那契查找就像用斐波那契数列的“黄金比例”来切蛋糕,聪明地决定每次切的位置,快速找到目标。
我们继续深入讲解斐波那契查找,帮你更全面理解它的工作原理和应用场景。
斐波那契查找的详细过程
假设你有一个升序排列的数组,长度为n
,你要查找目标值key
。
1. 找到最小的斐波那契数F[k]
,使得F[k] >= n
- 斐波那契数列是:1, 1, 2, 3, 5, 8, 13, 21, 34, …
- 例如,数组长度是20,找到
F[8] = 21
,因为21 >= 20。
2. 初始化变量
offset = -1
,表示已排除的前面元素的索引k
是斐波那契数的索引
3. 进行查找
- 比较
arr[min(offset + F[k-2], n-1)]
和key
- 如果
key
大于该值,说明目标在右边,更新:offset = offset + F[k-2]
k = k - 1
- 如果
key
小于该值,说明目标在左边,更新:k = k - 2
- 如果相等,查找成功,返回索引。
4. 重复步骤3,直到k
变为0或找到目标
斐波那契查找的示意图
假设数组长度是21,斐波那契数是21:
数组索引: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
斐波那契数: F[k] = 21
第一次比较位置: offset + F[k-2] = -1 + F[6] = -1 + 13 = 12
- 比较
arr[12]
和key
,根据结果调整offset
和k
,继续查找。
斐波那契查找的优缺点
优点 | 缺点 |
---|---|
只用加减法,不用除法,计算简单 | 需要预先计算斐波那契数列 |
利用黄金分割比例,查找效率高 | 适用范围有限,数组必须有序 |
对某些存储结构访问效率更好 | 实现比二分查找稍复杂 |
生活中的类比
- 二分查找:像你把书架从中间分开找书。
- 斐波那契查找:像你用黄金比例分割书架,先去大约0.618处找,
如果没找到,再用更小的斐波那契数分割剩下的部分,
这样分割更符合自然规律,查找更高效。
斐波那契查找的应用场景
- 适合在计算除法代价高的硬件环境。
- 适合访问代价不均匀的存储结构,比如磁盘或分布式存储。
- 适合对有序数组进行快速查找。
代码示例(Python)
def fibonacci_search(arr, key):
n = len(arr)
# 初始化斐波那契数
fibMMm2 = 0 # (m-2)'th Fibonacci No.
fibMMm1 = 1 # (m-1)'th Fibonacci No.
fibM = fibMMm2 + fibMMm1 # m'th Fibonacci
# 找到最小的fibM >= n
while fibM < n:
fibMMm2 = fibMMm1
fibMMm1 = fibM
fibM = fibMMm2 + fibMMm1
offset = -1
while fibM > 1:
i = min(offset + fibMMm2, n - 1)
if arr[i] < key:
fibM = fibMMm1
fibMMm1 = fibMMm2
fibMMm2 = fibM - fibMMm1
offset = i
elif arr[i] > key:
fibM = fibMMm2
fibMMm1 = fibMMm1 - fibMMm2
fibMMm2 = fibM - fibMMm1
else:
return i
if fibMMm1 and offset + 1 < n and arr[offset + 1] == key:
return offset + 1
return -1
# 测试
arr = [10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100]
key = 85
index = fibonacci_search(arr, key)
print(f"元素{key}的索引是: {index}")