递归和动态规划的区别,最好用例子,
在递归中,对数组的处理,比如说
const arr = [1, 2, 3, 4, 5, 6, 7]
function sum(i) {
return i === 0 ? arr[0] : (arr[i] + sum(i - 1))
}
终止条件是 i
等于 0
,是否也可以把终止条件设置为 i = arr.length-1
,只不过 i===0
更易于理解和操作,不知道我理解是否正确?
递归和动态规划的区别,最好用例子,
在递归中,对数组的处理,比如说
const arr = [1, 2, 3, 4, 5, 6, 7]
function sum(i) {
return i === 0 ? arr[0] : (arr[i] + sum(i - 1))
}
终止条件是 i
等于 0
,是否也可以把终止条件设置为 i = arr.length-1
,只不过 i===0
更易于理解和操作,不知道我理解是否正确?
这俩没太大关系。。动态规划是解决问题的一种思路,递归是手段。
假如你要实现一个复杂页面,你有以下两种方案:
页面=模块A + 模块B = 组件A + 组件B + ... = 文件A + 文件B + ... = 方法A + 方法B + 方法C + ...
你面对复杂问题知道了首先考虑解决这个问题,我需要什么东西,把一个复杂问题转移成若干子问题来实现。再回头看算法题,比如爬楼梯问题,假设n级台接,你一次只能爬一级或两级。
从第一级台接开始数可能性的话,思维的复杂度太大,所以反过来看,要得到f(n),我需要什么。反过来看可以发现最后一级台阶只能从n-1级台接和n-2级台接到达,所以得到转移函数:f(n) = f(n-1) + f(n-2)
可以得到代码如下:
const getCount = (n) => {
if(n===1) return 1
if(n===2) return 2
return getCount(n-1) + getCount(n-2)
}
但是这么写会有太多重复计算,你计算getCount(n-1)
的时候已经算过getCount(n-2)
了,所以可以空间换时间,缓存计算过的结果:
const getCount = (n) => {
const cache = {0: 1, 2: 2}
const calc = (n) => {
if(n in cache) return cache[n]
const rst = calc(n-1) + calc(n-2)
cache[n] = rst
return cache[n]
}
return getCount(n-1) + getCount(n-2)
}
但是这么写又有一个问题,空间复杂度太高,cache中缓存了从1到n的结果,其实只需要n和n-1,所以迭代写法:
const getCount = (n) => {
if(n===1) return 1
if(n===2) return 2
let past2 = 1
let prev = 2;
for(let i=3; i<=n; i++) {
const val = past2 + prev
past2 = prev
prev = val
}
return prev
}
8 回答5.8k 阅读✓ 已解决
9 回答9.2k 阅读
6 回答4.7k 阅读✓ 已解决
5 回答3.5k 阅读✓ 已解决
3 回答10.3k 阅读✓ 已解决
4 回答7.9k 阅读✓ 已解决
7 回答9.8k 阅读
递归,官话:
俗话:自己调用自己
函数接受某一个具有“范围”特性的参数,将其“范围”缩小,就可以借助自身不断的将其缩小,最终缩小到一定程度认为是目标结果。
动态规划,官话:
俗话:走过的路我就不走了,已知的结论我就不去再运算了。
递归和动态规划从大面上说可能不是针对同一种问题,但是概念上或者说思路上是有一些相关的。直接一点,动态规划解决问题的时候会用到递归调用。当然也有的DP不需要递归,依赖一个数据结构。具体可以参考一下斐波那契的递归和非递归写法。
递归和DP可能都有一个“依赖上一次”或者“依赖之前”的感觉,递归可能就是传统的从调用栈的角度上来体现这种上下依赖关系(递归太深可能会爆炸),同样的,一些DP算法使用特殊的数据结构来维系上一次或者之前的结果(数据太多一样也有爆炸的可能性)。
至于给出的例子,我认为不可以改,你可以试一下改了之后sum(0)的结果。