509. 斐波那契数
斐波那契数 (通常用
F(n)表示)形成的序列称为 斐波那契数列 。该数列由0和1开始,后面的每一项数字都是前面两项数字的和。也就是:F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定
n,请计算F(n)。
cpp
class Solution {
public:
int fib(int n) {
if(n < 2) return n; // 1. 特殊情况处理
vector<int> dp(n + 1); // 2. 创建 DP 表(状态数组)
dp[0] = 0; // 3. 初始化状态
dp[1] = 1;
for(int i = 2; i <= n; i++){ // 4. 状态转移(填表)
dp[i] = dp[i-1] + dp[i-2]; // 递推公式
}
return dp[n]; // 5. 返回结果
}
};
总结
1. 解题五步法分析
- 确定 dp 数组含义:
dp[i]定义为第i个斐波那契数的值。 - 确定递推公式:题目直接给了,
dp[i] = dp[i-1] + dp[i-2]。这是 DP 的灵魂。 - dp 数组初始化:
dp[0]=0, dp[1]=1。这是递推的基石。 - 确定遍历顺序:因为
dp[i]依赖前面的dp[i-1]和dp[i-2],所以必须 从前向后 遍历。 - 举例推导:比如 n=4,序列是 0, 1, 1, 2, 3。手动推导验证代码逻辑是否正确。
2. 复杂度分析
- 时间复杂度:O(n)。只有一个循环,执行 n−1次。
- 空间复杂度:O(n)。开辟了一个长度为 n+1的数组。
70. 爬楼梯
假设你正在爬楼梯。需要
n阶你才能到达楼顶。每次你可以爬
1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?
cpp
class Solution {
public:
int climbStairs(int n) {
if (n <= 2) return n;
// 1. 定义 DP 数组
// dp[i] 表示爬到第 i 阶楼梯的方法数
vector<int> dp(n + 1);
// 2. 初始化状态
// dp[0] 本题无意义,从 dp[1] 开始
dp[1] = 1; // 1阶楼梯:1种方法 (1)
dp[2] = 2; // 2阶楼梯:2种方法 (1+1, 2)
// 3. 状态转移(填表)
// 从第 3 阶开始遍历
for (int i = 3; i <= n; i++) {
// 递推公式:
// 要么是从第 i-1 阶迈 1 步上来的
// 要么是从第 i-2 阶迈 2 步上来的
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
};
总结
1. 思维推导
- 最后一步:想要跳到第
n阶,最后一步只有两种选择:- 从
n-1阶跳 1 步。 - 从
n-2阶跳 2 步。
- 从
- 推导:既然最后一步只有这两种走法,那么走到
n阶的方法数 = 走到n-1的方法数 + 走到n-2的方法数。 - 结论:这就是斐波那契数列的递推公式。
2. 复杂度分析
- 时间复杂度:O(n),一次循环。
- 空间复杂度:O(n),用数组存储了所有状态。
746. 使用最小花费爬楼梯
给你一个整数数组
cost,其中cost[i]是从楼梯第i个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为
0或下标为1的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
cpp
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
// 1. 定义 DP 数组
// dp[i] 表示:到达第 i 个台阶顶端(即站在第 i 个位置)所需要的最小花费
// 大小为 n+1,因为我们要到达的是数组末尾的"楼顶"(下标 n)
vector<int> dp(n + 1, 0);
// 2. 初始化
// 题目说可以从下标 0 或 1 开始,意味着站在起点是不花钱的
// dp[0] = 0, dp[1] = 0 (vector初始化时已设为0)
// 3. 状态转移
// 从第 2 层开始遍历,直到楼顶 n
for (int i = 2; i <= n; i++) {
// 想要到达第 i 层,只有两种来源:
// 1. 从第 i-1 层迈 1 步上来(花费:到达 i-1 的最小花费 + 踩 i-1 的费用)
// 2. 从第 i-2 层迈 2 步上来(花费:到达 i-2 的最小花费 + 踩 i-2 的费用)
dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
return dp[n];
}
};
总结
1. 核心思路对比
- 普通爬楼梯:
dp[i]存的是方法数(求和)。 - 最小花费爬楼梯:
dp[i]存的是最小花费(求 Min)。
2. 两种思维模型
-
本题的定义:
dp[i]代表 到达 第i级台阶的最小花费。- 起点的花费为 0。
- 花费产生在"迈步"的过程中(
dp[i-1] + cost[i-1])。 - 这种思路符合题目"支付费用才能向上爬"的描述,逻辑顺畅。
-
另一种定义:
dp[i]代表 经过 第i级台阶时的最小花费(包含踩上去的钱)。- 那么初始化就是
dp[0]=cost[0],dp[1]=cost[1]。 - 递推公式是
dp[i] = min(dp[i-1], dp[i-2]) + cost[i]。 - 最后结果是
min(dp[n-1], dp[n-2])。 - 这种写法容易在初始化和最后返回值上纠结。
- 那么初始化就是
3. 复杂度分析
- 时间复杂度:O(n),一次遍历。
- 空间复杂度:O(n),使用了长度为 n+1的数组。