【每日算法C#】爬楼梯问题 LeetCode
public int ClimbStairs(int n)
{int[] dp = new int[n + 1]; // 创建DP数组dp[0] = 1; // 基础情况:0阶有1种方法(不动)dp[1] = 1; // 基础情况:1阶有1种方法(爬1步)for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2]; // 状态转移方程}return dp[n]; // 返回n阶的解
}
关键点解析
-
动态规划思想:
- 将大问题分解为小问题(到达第 i 阶的方法数)
- 存储子问题解避免重复计算
-
状态定义:
dp[i]
表示爬到第 i 阶楼梯的方法总数
-
状态转移方程:
dp[i] = dp[i-1] + dp[i-2]
- 解释:到达第 i 阶只能从:
- 第 i-1 阶爬 1 步
- 第 i-2 阶爬 2 步
-
基础情况:
dp[0] = 1
:起点,不需要移动dp[1] = 1
:只有 1→1 这一种方式
复杂度分析
维度 | 值 | 说明 |
---|---|---|
时间复杂度 | O(n) | 单次遍历完成计算 |
空间复杂度 | O(n) | 需要长度为 n+1 的数组 |
示例计算(n=4)
dp[0] = 1
dp[1] = 1
dp[2] = dp[1] + dp[0] = 1 + 1 = 2
dp[3] = dp[2] + dp[1] = 2 + 1 = 3
dp[4] = dp[3] + dp[2] = 3 + 2 = 5
空间优化(O(1)空间):
public int ClimbStairs(int n)
{if (n <= 1) return 1;int a = 1, b = 1;for (int i = 2; i <= n; i++) {int temp = a + b;a = b;b = temp;}return b;
}
动态规划(Dynamic Programming)
动态规划(Dynamic Programming,简称DP)是一种解决复杂问题的算法思想,其核心理念可概括为以下五个关键点:
1. 分治思想(Divide and Conquer)
- 本质:将大问题分解为相互关联的子问题
- 特点:子问题不是独立的,而是相互重叠的
- 类比:像拼图游戏,先解决局部小块,再组合成完整画面
- 示例:斐波那契数列中
fib(n) = fib(n-1) + fib(n-2)
2. 最优子结构(Optimal Substructure)
- 核心原则:问题的最优解包含其子问题的最优解
- 关键特征:局部最优解能推导出全局最优解
- 反例:求最长路径问题(不满足最优子结构)
- 示例:背包问题中,包含当前物品的最优解 = 当前物品价值 + 剩余容量最优解
3. 重叠子问题(Overlapping Subproblems)
- 核心特征:不同子问题会重复计算相同内容
- 解决方法:存储子问题解避免重复计算
- 对比:
方法 计算次数 时间复杂度 递归 指数级 O(2ⁿ) DP 线性 O(n) - 示例:计算斐波那契数列时,fib(3)会被多次计算
4. 状态存储(Memoization)
- 核心机制:用表格(通常数组)存储子问题解
- 两种实现:
- 自顶向下:递归+记忆化(Top-down)
- 自底向上:迭代填表(Bottom-up)
5. 状态转移方程(State Transition Equation)
- 核心引擎:定义子问题之间的关系
- 通用形式:
dp[i] = F(dp[i-1], dp[i-2], ..., 输入参数)
- 设计步骤:
- 定义状态:
dp[i]
表示什么 - 初始化:边界条件设置
- 递推关系:如何从已知状态推导新状态
- 求解目标:
dp[n]
或max/min(dp)
- 定义状态: