目录
[1. 核心思路:拆解问题](#1. 核心思路:拆解问题)
[2. 代码实现(Java 版)](#2. 代码实现(Java 版))
[3. 关键知识点](#3. 关键知识点)
[二、杨辉三角:二维 DP 的入门实践](#二、杨辉三角:二维 DP 的入门实践)
[1. 核心思路:二维状态转移](#1. 核心思路:二维状态转移)
[2. 代码实现(Java 版)](#2. 代码实现(Java 版))
[3. 关键知识点](#3. 关键知识点)
前言
动态规划(DP)是算法面试的高频考点,很多同学刚接触时,总觉得它 "又抽象又难理解"。其实入门阶段,两道经典题就能帮你捅破这层窗户纸:一道是《爬楼梯》,一道是《杨辉三角》。
今天我们就从这两道题入手,从问题拆解、状态定义、转移方程一步步讲透,帮你建立 DP 的基本思维框架。
一、爬楼梯:从斐波那契到动态规划
题目描述
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
1. 核心思路:拆解问题
我们先从一个简单的例子入手:
- 爬到第
1阶:只有1种方法(直接爬 1 阶) - 爬到第
2阶:有2种方法(1+1 / 2) - 爬到第
3阶:最后一步要么从第2阶爬 1 阶上来,要么从第1阶爬 2 阶上来 → 方法数 =f(2) + f(1) - 爬到第
n阶:同理,方法数 =f(n-1) + f(n-2)
这就是典型的动态规划递推关系:
- 状态定义 :
dp[i]表示爬到第i阶的方法数 - 转移方程 :
dp[i] = dp[i-1] + dp[i-2] - 初始条件 :
dp[1] = 1,dp[2] = 2
2. 代码实现(Java 版)
java
运行
public class ClimbStairs {
// 动态规划基础版(数组存储)
public int climbStairs(int n) {
if (n <= 2) return n;
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// 优化版(滚动变量,空间O(1))
public int climbStairsOptimized(int n) {
if (n <= 2) return n;
int a = 1, b = 2;
for (int i = 3; i <= n; i++) {
int temp = a + b;
a = b;
b = temp;
}
return b;
}
public static void main(String[] args) {
ClimbStairs solution = new ClimbStairs();
System.out.println(solution.climbStairs(5)); // 输出:8
System.out.println(solution.climbStairsOptimized(5)); // 输出:8
}
}
3. 关键知识点
- 这道题本质就是斐波那契数列,但用 DP 的思路更直观。
- 时间复杂度:O (n),空间复杂度可以优化到 O (1)(只保留前两个状态)。
- 面试拓展:如果每次可以爬
k阶,转移方程就变成dp[i] = sum(dp[i-k] ... dp[i-1]),核心思想不变。
二、杨辉三角:二维 DP 的入门实践
题目描述
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。
1. 核心思路:二维状态转移
杨辉三角的核心规律:
- 第
i行有i个元素(从第 0 行开始计数) - 每行的首尾元素都是
1 - 中间元素
triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
这是典型的二维动态规划:
- 状态定义 :
triangle[i][j]表示第i行第j列的元素值 - 转移方程 :
triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j](首尾元素除外) - 初始条件 :每行首尾元素为
1
2. 代码实现(Java 版)
java
运行
import java.util.ArrayList;
import java.util.List;
public class PascalTriangle {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> triangle = new ArrayList<>();
if (numRows == 0) return triangle;
// 第0行
List<Integer> firstRow = new ArrayList<>();
firstRow.add(1);
triangle.add(firstRow);
// 从第1行开始生成
for (int i = 1; i < numRows; i++) {
List<Integer> prevRow = triangle.get(i - 1);
List<Integer> currRow = new ArrayList<>();
// 首尾元素为1
currRow.add(1);
// 中间元素由上一行计算而来
for (int j = 1; j < i; j++) {
currRow.add(prevRow.get(j - 1) + prevRow.get(j));
}
currRow.add(1);
triangle.add(currRow);
}
return triangle;
}
public static void main(String[] args) {
PascalTriangle solution = new PascalTriangle();
List<List<Integer>> result = solution.generate(5);
for (List<Integer> row : result) {
System.out.println(row);
}
/* 输出:
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
*/
}
}
3. 关键知识点
- 时间复杂度:O (n²),每个元素只计算一次。
- 空间复杂度:O (n²),存储整个三角结构;如果只需要第
n行,可以优化到 O (n)。 - 面试拓展:杨辉三角的变种题,如求第
n行的元素和、求第n行第k列的元素,都可以用同样的 DP 思路解决。