1. 📚 问题描述
斐波那契数列(Fibonacci Sequence)是一个经典的数学序列,定义如下:
- 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, ...
2. 🎯 动态规划思想
与递归相比,动态规划通过存储子问题的解避免重复计算,核心要素包括:
| 要素 | 说明 | 在本题中的应用 |
|---|---|---|
| 🔑 最优子结构 | 问题的最优解包含子问题的最优解 | F(n) 的解依赖于 F(n-1) 和 F(n-2) |
| 💾 记忆化 | 将已计算的结果存储起来避免重复计算 | 数组或变量存储中间结果 |
| ⚡ 自底向上 | 从基础情况开始逐步构建到目标问题 | 从 F(0) 和 F(1) 开始计算到 F(n) |
| 🚀 空间优化 | 根据状态依赖关系降低空间复杂度 | 从 O(n) 优化到 O(1) |
3. 📊 算法对比
| 方法 | 时间复杂度 | 空间复杂度 | 优缺点 |
|---|---|---|---|
| 递归(暴力) | O(2ⁿ) | O(n) | ❌ 重复计算极多,效率极低,指数级爆炸 |
| 动态规划(数组) | O(n) | O(n) | ✅ 高效,易理解,保留所有中间结果 |
| 动态规划(降维) | O(n) | O(1) | ✅ 最优解,空间效率最高,仅保留必要状态 |
| 矩阵快速幂 | O(log n) | O(1) | ⚡ 最快但实现复杂,适合超大n,理论最优 |
4. 📝 示例演示:计算 F(6)
4.1 方法1:使用数组(O(n)空间)
java
初始化数组:arr[0]=0, arr[1]=1
i=2: arr[2] = arr[1] + arr[0] = 1 + 0 = 1
i=3: arr[3] = arr[2] + arr[1] = 1 + 1 = 2
i=4: arr[4] = arr[3] + arr[2] = 2 + 1 = 3
i=5: arr[5] = arr[4] + arr[3] = 3 + 2 = 5
i=6: arr[6] = arr[5] + arr[4] = 5 + 3 = 8
结果:F(6) = 8
数组状态:[0, 1, 1, 2, 3, 5, 8]
4.2 方法2:降维优化(O(1)空间)
java
初始化:i=0 (F₀), j=1 (F₁)
k=2: c = 0+1=1, 更新 i=1, j=1 (计算F₂)
k=3: c = 1+1=2, 更新 i=1, j=2 (计算F₃)
k=4: c = 1+2=3, 更新 i=2, j=3 (计算F₄)
k=5: c = 2+3=5, 更新 i=3, j=5 (计算F₅)
k=6: c = 3+5=8, 更新 i=5, j=8 (计算F₆)
结果:F(6) = 8
内存占用:仅需3个int变量!
5. 🌟 应用场景
- 🪜 爬楼梯问题:每次爬1或2阶,求到达n阶的方法数
- 🐰 兔子繁殖问题:经典的斐波那契起源问题
- 🌻 黄金分割:相邻两项比值趋近于黄金分割比 φ ≈ 1.618
- 🎨 艺术设计:斐波那契螺旋线在自然界和艺术中广泛存在
- 📊 算法优化:学习动态规划的经典入门案例
- 🍃 植物生长:树叶的排列、花瓣数量等自然现象
- 📈 金融分析:技术分析中的斐波那契回调线
6. 💻 代码实现详解
6.1 方法1:动态规划 - 使用数组存储
java
/**
* 使用数组实现斐波那契数列
* 时间复杂度:O(n)
* 空间复杂度:O(n)
*/
public static int fibonacci(int n) {
// 创建数组存储所有斐波那契数
int[] arr = new int[n + 1];
// 初始化前两项
arr[0] = 0;
arr[1] = 1;
// 处理边界情况
if (n < 2) {
return arr[n];
}
// 自底向上计算每一项
for (int i = 2; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2]; // 状态转移方程
}
return arr[n];
}
优点:
- 逻辑清晰,易于理解
- 保留了所有中间结果,便于调试
- 如果需要多次查询不同的
F(i),只需计算一次
缺点:
- 空间复杂度较高,对于超大
n可能内存不足 - 计算单个
F(n)时存储了大量不必要的中间结果
6.2 方法2:动态规划 - 空间优化(降维)
java
/**
* 使用降维优化实现斐波那契数列
* 时间复杂度:O(n)
* 空间复杂度:O(1) - 只使用常数个变量
*/
public static int fibonacciJW(int n) {
// 处理边界情况:F(0) = 0
if (n == 0) {
return 0;
}
// 处理边界情况:F(1) = 1
if (n == 1) {
return 1;
}
// 初始化两个变量:i表示F(k-2),j表示F(k-1)
int i = 0; // F(0)
int j = 1; // F(1)
// 从第2项开始滚动计算到第n项
for (int k = 2; k <= n; k++) {
int c = i + j; // 计算当前项 F(k) = F(k-1) + F(k-2)
i = j; // 向前滚动:F(k-2) = 旧的F(k-1)
j = c; // 向前滚动:F(k-1) = 新计算的F(k)
}
// 循环结束后,j 就是 F(n)
return j;
}
核心思想 :
观察到计算 F(n) 只需要 F(n-1) 和 F(n-2),不需要保存所有历史值。使用两个变量滚动更新 ,将空间复杂度从 O(n) 降到 O(1)。
变量含义:
i:表示F(k-2),前前一项j:表示F(k-1),前一项c:表示F(k),当前项 = i + j
⚠️ 注意事项:
- 变量更新顺序很重要:先算
c,再更新i和j - 循环结束后,
j存储的就是F(n) - 如果需要保留所有中间结果,应使用方法1
7. 🧪 测试方法
java
public static void main(String[] args) {
// 测试用例:计算 F(21)
// 期望输出:10946(两种方法结果相同)
System.out.println(fibonacci(21)); // 使用数组方法
System.out.println(fibonacciJW(21)); // 使用降维方法
}
测试结果验证:
10946
10946
F(21) 完整数列 :
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946
8. 📈 复杂度对比总结
| 实现方式 | 时间复杂度 | 空间复杂度 | 推荐场景 |
|---|---|---|---|
| 数组存储 | O(n) | O(n) | 需要多次查询不同F(i),或需要调试中间状态 |
| 降维优化 | O(n) | O(1) | 仅需计算单个F(n),追求极致空间效率 |
| 矩阵快速幂 | O(log n) | O(1) | n极大(如10⁹)时,对时间要求苛刻的场景 |
9. 🔧 扩展思考
9.1 如何选择实现方式?
- n ≤ 10⁶ :优先使用降维优化方法,简单高效
- 需要多次查询 :使用数组方法,空间换时间
- n极大(10⁹级别) :考虑矩阵快速幂 或通项公式
- 教学演示 :从递归 → 数组DP → 降维逐步演进
9.2 从斐波那契到动态规划
斐波那契是学习DP的最佳入门案例:
- ✅ 有明确的状态转移方程
- ✅ 具备最优子结构特性
- ✅ 存在重叠子问题(递归会重复计算)
- ✅ 容易进行空间优化
9.3 性能测试建议
java
// 测试大数值性能(注意递归会栈溢出)
long start = System.currentTimeMillis();
System.out.println(fibonacciJW(1000000)); // 计算F(100万)
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - start) + "ms"); // 约10-20ms
10. ✅ 总结
10.1 核心要点
- 状态转移 :
F(n) = F(n-1) + F(n-2)是动态规划的精髓 - 自底向上:从已知到未知,避免递归的重复计算
- 空间优化 :观察状态依赖,将
O(n)空间降至O(1) - 边界处理 :
F(0)=0和F(1)=1是计算基础
10.2 学习建议
- 初学者:先掌握数组方法,理解DP流程
- 进阶:挑战降维优化,理解状态压缩思想
- 高阶:尝试实现矩阵快速幂,探索O(log n)解法
- 实践:用斐波那契解决爬楼梯、兔子繁殖等实际问题
10.3 代码优势
- 清晰注释:每一步都有详细说明
- 两种实现:对比学习,理解空间优化
- 边界完备 :处理了
n=0和n=1的特殊情况 - 教学友好:适合作为动态规划的入门案例
11. 📚 相关题目推荐
| 题目 | 难度 | 关联度 | 核心思想 |
|---|---|---|---|
| LeetCode 70 - 爬楼梯 | Easy | ★★★★★ | 斐波那契的直接应用 |
| LeetCode 509 - 斐波那契数 | Easy | ★★★★★ | 本题的官方版本 |
| LeetCode 746 - 使用最小花费爬楼梯 | Easy | ★★★★☆ | 变体DP问题 |
| LeetCode 1137 - 第N个泰波那契数 | Easy | ★★★☆☆ | 三项递推,思路类似 |