LeetCode刷题指南
第 0 章 hot100
0.1 哈希
0.2 双指针
0.3 滑动窗口
0.4 子串
0.5 普通数组
0.6 矩阵
0.7 链表
0.8 二叉树
0.9 图论
0.10 回溯
0.11 二分查找
0.12 栈
0.13 堆
0.14 贪心算法
0.15 动态规划
0.16 多维动态规划
0.17 技巧
第0-1章 面试经典150
0.1 数组/字符串
0.2 双指针
0.3 滑动窗口
链表
二叉树
第 1 章 最易懂的贪心算法
1.1 算法解释
1.2 分配问题
1.3 区间问题
1.4 练习
第 2 章 玩转双指针
2.1 算法解释
2.2 Two Sum
2.3 归并两个有序数组
2.4 滑动窗口
2.5 快慢指针
2.6 练习
第 3 章 居合斩!二分查找
3.1 算法解释
3.2 求开方
3.3 查找区间
3.4 查找峰值
3.5 旋转数组查找数字
3.6 练习
第 4 章 千奇百怪的排序算法
4.1 常用排序算法
4.2 快速选择
4.3 桶排序
4.4 练习
第 5 章 一切皆可搜索
5.1 算法解释
5.2 深度优先搜索
5.3 回溯法
5.4 广度优先搜索
5.5 练习
第 6 章 深入浅出动态规划
6.1 算法解释
6.2 基本动态规划:一维
6.3 基本动态规划:二维
6.4 分割类型题
6.5 子序列问题
6.6 背包问题
6.7 字符串编辑
6.8 股票交易
6.9 练习
第 7 章 化繁为简的分治法
7.1 算法解释
7.2 表达式问题
7.3 练习
第 8 章 巧解数学问题
8.1 引言
8.2 公倍数与公因数
8.3 质数
8.4 数字处理
8.5 随机与取样
8.6 练习
第 9 章 神奇的位运算
9.1 常用技巧
9.2 位运算基础问题
9.3 二进制特性
9.4 练习
第 10 章 妙用数据结构
10.1 C++ STL
10.2 Python 常用数据结构
10.3 数组
10.4 栈和队列
10.5 单调栈
10.6 优先队列
10.7 双端队列
10.8 哈希表
10.9 多重集合和映射
10.10 前缀和与积分图
10.11 练习
第 11 章 令人头大的字符串
11.1 引言
11.2 字符串比较
11.3 字符串理解
11.4 字符串匹配
11.5 练习
第 12 章 指针三剑客之一:链表
12.1 数据结构介绍
12.2 链表的基本操作
12.3 其它链表技巧
12.4 练习
第 13 章 指针三剑客之二:树
13.1 数据结构介绍
13.2 树的递归
13.3 层次遍历
13.4 前中后序遍历
13.5 二叉查找树
13.6 字典树
13.7 练习
第 14 章 指针三剑客之三:图
14.1 数据结构介绍
14.2 二分图
14.3 拓扑排序
14.4 练习
第 15 章 更加复杂的数据结构
15.1 引言
15.2 并查集
15.3 复合数据结构
15.4 练习
第16章 面试题
第 17 章 十大经典排序算法
README
本文档使用 MrDoc 发布
-
+
首页
6.2 基本动态规划:一维
# 6.2 基本动态规划:一维 ## [70. Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) ### 题目描述 给定 $$n$$ 节台阶,每次可以走一步或走两步,求一共有多少种方式可以走完这些台阶。 ### 输入输出样例 输入是一个数字,表示台阶数量;输出是爬台阶的总方式。 ``` Input: 3 Output: 3 ``` 在这个样例中,一共有三种方法走完这三节台阶:每次走一步;先走一步,再走两步;先走两步,再走一步。 ### 题解 这是十分经典的斐波那契数列题。定义一个数组 dp,dp[i] 表示走到第 i 阶的方法数。因为我们每次可以走一步或者两步,所以第 i 阶可以从第 i-1 或 i-2 阶到达。换句话说,走到第 i 阶的方法数即为走到第 i-1 阶的方法数加上走到第 i-2 阶的方法数。这样我们就得到了状态转移方程 $$dp[i] = dp[i-1] + dp[i-2]$$。注意边界条件的处理。 有的时候为了方便处理边界情况,我们可以在构造 dp 数组时多留一个位置,用来处理初始状态。本题即多留了一个第 0 阶的初始位置。 ```py class Solution: def climbStairs(self, n: int) -> int: dp = [0] * (n+1) dp[0] = 1 dp[1] = 1 for i in range(2,n+1): dp[i] = dp[i-1] + dp[i-2] return dp[n] ``` 进一步的,我们可以对动态规划进行空间压缩。因为 dp[i] 只与 dp[i-1] 和 dp[i-2] 有关,因此可以只用两个变量来存储 dp[i-1] 和 dp[i-2],使得原来的 $$O(n)$$ 空间复杂度优化为 $$O(1)$$ 复杂度。 ```py class Solution: def climbStairs(self, n: int) -> int: if n < 2: return 1 prev1, prev2 = 1, 1 for i in range(2, n+1): current = prev1 + prev2 prev2 = prev1 prev1 = current return prev1 ``` ## [198. House Robber](https://leetcode.com/problems/house-robber/) ### 题目描述 假如你是一个劫匪,并且决定抢劫一条街上的房子,每个房子内的钱财数量各不相同。如果你抢了两栋相邻的房子,则会触发警报机关。求在不触发机关的情况下最多可以抢劫多少钱。 ### 输入输出样例 输入是一个一维数组,表示每个房子的钱财数量;输出是劫匪可以最多抢劫的钱财数量。 ``` Input: [2,7,9,3,1] Output: 12 ``` 在这个样例中,最多的抢劫方式为抢劫第 1、3、5 个房子。 ### 题解 定义一个数组 dp,dp[i] 表示抢劫到第 i 个房子时,可以抢劫的最大数量。我们考虑 dp[i],此时可以抢劫的最大数量有两种可能,一种是我们选择不抢劫这个房子,此时累计的金额即为 dp[i-1];另一种是我们选择抢劫这个房子,那么此前累计的最大金额只能是 dp[i-2],因为我们不能够抢劫第 i-1 个房子,否则会触发警报机关。因此本题的状态转移方程为 dp[i] = max(dp[i-1], nums[i-1] + dp[i-2])。 ```py class Solution: def rob(self, nums: List[int]) -> int: n = len(nums) if n == 0: return 0 if n <= 2: return max(nums) dp = [0] * n dp[0] = nums[0] dp[1] = max(nums[0], nums[1]) for i in range(2, n): dp[i] = max(dp[i-1], dp[i-2] + nums[i]) return dp[-1] ``` 同样的,我们可以像题目 70 那样,对空间进行压缩。 ```py class Solution: def rob(self, nums: List[int]) -> int: n = len(nums) if n == 0: return 0 if n <= 2: return max(nums) prev_prev = nums[0] prev = max(nums[0], nums[1]) for i in range(2,n): cur = max(prev_prev + nums[i], prev) prev_prev = prev prev = cur return cur ``` ## [413. Arithmetic Slices](https://leetcode.com/problems/arithmetic-slices/) ### 题目描述 给定一个数组,求这个数组中连续且等差的子数组一共有多少个。 ### 输入输出样例 输入是一个一维数组,输出是满足等差条件的连续子数组个数。 ``` Input: nums = [1,2,3,4] Output: 3 ``` 在这个样例中,等差数列有 [1,2,3]、[2,3,4] 和 [1,2,3,4]。 ### 题解 因为要求是等差数列,可以很自然地想到子数组必定满足 num[i] - num[i-1] = num[i-1] - num[i-2]。这里我们对于 dp 数组的定义是以 i 结尾的,满足该条件的子数组数量。因为等差子数组可以在任意一个位置终结,所以我们需要对 dp 数组求和进行子数组统计。 ```py class Solution: def numberOfArithmeticSlices(self, nums: List[int]) -> int: n = len(nums) dp = [0] * n for i in range(2,n): if nums[i] - nums[i-1] == nums[i-1] - nums[i-2]: dp[i] = dp[i-1] + 1 return sum(dp) ```
嘉心糖糖
2025年3月18日 20:02
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
PDF文档(打印)
分享
链接
类型
密码
更新密码