From 64c27f7bf4569f454098d482a2da742bf44178a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=B0=8F=E7=84=B6=E5=AD=90?= <1252198830@qq.com> Date: Mon, 9 Nov 2020 23:57:32 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E7=94=B5=E5=AD=90=E4=B9=A6=E5=8B=98?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- thinkings/dynamic-programming.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/thinkings/dynamic-programming.md b/thinkings/dynamic-programming.md index d1b7b4aaa..5bc77853d 100644 --- a/thinkings/dynamic-programming.md +++ b/thinkings/dynamic-programming.md @@ -6,7 +6,7 @@ 递归是指在函数的定义中使用函数自身的方法。 -算法中使用递归可以很简单地完成一些用循环实现的功能,比如二叉树的左中右序遍历。递归在算法中有非常广泛的使用,包括现在日趋流行的函数式编程。 +算法中使用递归可以很简单地完成一些用循环实现的功能,比如二叉树的先中后序遍历。递归在算法中有非常广泛的使用,包括现在日趋流行的函数式编程。 有意义的递归算法会把问题分解成规模缩小的同类子问题,当子问题缩减到寻常的时候,就可以知道它的解。然后建立递归函数之间的联系即可解决原问题,这也是我们使用递归的意义。准确来说, 递归并不是算法,它是和迭代对应的一种编程方法。只不过,由于隐式地借助了函数调用栈,因此递归写起来更简单。 @@ -33,7 +33,7 @@ def f(n): ### 递归中的重复计算 -递归中可能存在这么多的重复计算,为了消除这种重复计算,一种简单的方式就是记忆化递归。即一边递归一边使用“记录表”(比如哈希表或者数组)记录我们已经计算过的情况,当下次再次碰到的时候,如果之前已经计算了,那么直接返回即可,这样就避免了重复计算。而**动态规划中 DP 数组其实和这里“记录表”的作用是一样的**。 +递归中可能存在这么多的重复计算,为了消除这种重复计算,一种简单的方式就是记忆化递归。即一边递归一边使用“记录表”(比如哈希表或者数组)记录我们已经计算过的情况,当下次再次碰到的时候,如果之前已经计算了,那么直接返回即可,这样就避免了重复计算。其实在**动态规划中,DP 数组和这里“记录表”的作用是一样的**。 ### 递归的时间复杂度分析 @@ -59,9 +59,9 @@ def f(n): 如果你已经熟悉了递归的技巧,那么使用递归解决问题非常符合人的直觉,代码写起来也比较简单。这个时候我们来关注另一个问题 - **重复计算** 。我们可以通过分析(可以尝试画一个递归树),可以看出递归在缩小问题规模的同时**是否可能会重复计算**。 [279.perfect-squares](../problems/279.perfect-squares.md) 中 我通过递归的方式来解决这个问题,同时内部维护了一个缓存来存储计算过的运算,这么做可以减少很多运算。 这其实和动态规划有着异曲同工的地方。 -> 小提示:如果你发现并没有重复计算,那么就没有必要用记忆化递归或者动态规划了。 +> 小提示:如果你发现并没有重复计算,那就没有必要用记忆化递归或者动态规划。 -因此动态规划就是枚举所以可能。不过相比暴力枚举,动态规划不会有重复计算。因此如何保证枚举时不重不漏是关键点之一。 递归由于使用了函数调用栈来存储数据,因此如果栈变得很大,那么会容易爆栈。 +因此动态规划就是枚举所有可能。不过相比暴力枚举,动态规划不会有重复计算。因此如何保证枚举时不重不漏是关键点之一。 由于递归使用了函数调用栈来存储数据,因此当栈变得很大的时候,很容易就会爆栈。 ### 爆栈 @@ -94,7 +94,7 @@ function sum(nums) { > [746. 使用最小花费爬楼梯](https://leetcode-cn.com/problems/min-cost-climbing-stairs/) 是这道题的换皮题, GrowingIO 前端工程师岗位考察过这个题目。 -由于上第 n 级台阶一定是从 n - 1 或者 n - 2 来的,因此 上第 n 级台阶的数目就是 `上 n - 1 级台阶的数目加上 n - 1 级台阶的数目`。 +由于上第 n 级台阶一定是从 n - 1 或者 n - 2 来的,因此 上第 n 级台阶的数目就是 `上 (n - 1) 级台阶的数目「加」上 (n - 2) 级台阶的数目`。 递归代码: @@ -152,7 +152,7 @@ function dp(n) { 这道题目是动态规划中最简单的问题了,因为只涉及到单个因素的变化,如果涉及到多个因素,就比较复杂了,比如著名的背包问题,挖金矿问题等。 -对于单个因素的,我们最多只需要一个一维数组即可,对于如背包问题我们需要二维数组等更高纬度。 +对于单个因素的,我们最多只需要一个一维数组即可,对于如背包问题我们需要二维数组等更高维度的数组。 爬楼梯我们并没有必要使用一维数组,而是借助两个变量来实现的,空间复杂度是 O(1)。代码: @@ -220,7 +220,7 @@ dp[n] = dp[n - 1] + dp[n - 2] 就是【状态转移方程】 #### 状态转移方程 -爬楼梯问题由于上第 n 级台阶一定是从 n - 1 或者 n - 2 来的,因此 上第 n 级台阶的数目就是 `上 n - 1 级台阶的数目加上 n - 1 级台阶的数目`。 +爬楼梯问题由于上第 n 级台阶一定是从 n - 1 或者 n - 2 来的,因此 上第 n 级台阶的数目就是 `上 (n - 1) 级台阶的数目「加」上 (n - 2) 级台阶的数目`。 上面的这个理解是核心, 它就是我们的状态转移方程,用代码表示就是 `f(n) = f(n - 1) + f(n - 2)`。 From 280e47fb734e0c2859155131b706e535675363bd Mon Sep 17 00:00:00 2001 From: lucifer Date: Tue, 10 Nov 2020 11:30:47 +0800 Subject: [PATCH 2/2] Update dynamic-programming.md --- thinkings/dynamic-programming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thinkings/dynamic-programming.md b/thinkings/dynamic-programming.md index 5bc77853d..c5687cf5e 100644 --- a/thinkings/dynamic-programming.md +++ b/thinkings/dynamic-programming.md @@ -152,7 +152,7 @@ function dp(n) { 这道题目是动态规划中最简单的问题了,因为只涉及到单个因素的变化,如果涉及到多个因素,就比较复杂了,比如著名的背包问题,挖金矿问题等。 -对于单个因素的,我们最多只需要一个一维数组即可,对于如背包问题我们需要二维数组等更高维度的数组。 +对于单个因素的,我们最多只需要一个一维数组即可,对于如背包问题我们需要二维甚至更高维度的数组。 爬楼梯我们并没有必要使用一维数组,而是借助两个变量来实现的,空间复杂度是 O(1)。代码: