动态规划和递归算法的区别

2,139 阅读3分钟

写在前面

本文举例都采用斐波那契数列

迭代:循环

最优子结构:原问题的最优解,可以通过求解子问题的最优解进而推出原问题的最优解

重叠子问题:在递归中,比如计算f(10),但是会把f(8)或者f(9)等等,进行多次计算;动态规划消除了重叠子问题,不进行多次重复计算

自顶向下:比如求解f(n),需要求得f(n-1),进而计算出f(n)的解;所以斐波那契数列递归时才会出现 f(n-1) + f(n-2)

自底向上:我们直接从最底下,最简单,问题规模最小的 f(1) 和 f(2) 开始往上推,直到推到我们想要的答案 f(20),这就是动态 规划的思路,这也是为什么动态规划一般都脱离了递归,而是由循环迭代完成计算。

顶部和底部: f(n)就相当于顶部,f(1)相当于底部

1. 动态规划和递归算法的区别

简单说动态规划是递归的优化版算法,动态规划把递归函数优化成存储+迭代+自底向上思想

递归函数 === 使用最小的空间存储 + 使用最小时间复杂度的循环迭代 + 自底向上的思想

// 不同点:
1. 动态规划将递归函数变成了循环迭代
2. 动态规划消除了递归产生的重叠子问题
3. 动态规划采用`自底向上`的思想,递归采用`自顶向下`思想

2. 案例分析

// 递归版斐波那契
function Fun(n){
	if (n==1 || n==2) return 1
    return Fun(n-1) + Fun(n-2)
}

递归版时间复杂度太高了,并且出现重叠子问题的重复计算问题;还有就是这里求解的Fun(n), 递归思想是自顶向下。

// 动态规划版斐波那契
function Fun(n){
    if(n < 1) return 0
    if (n==1 || n==2) return 1
    let dp = []
    
    // 初始最底部条件
    dp[1] = dp[2] = 1
    
    // 迭代
    for (let i = 3; i <= n; ++i){
    	// 递推公式
    	dp[i] = dp[i - 1] + dp[i - 2]
    }
    return dp[n]
}
console.log(Fun(4))

动态规划其实跟递归很相像,只是思想上动态规划是从底部开始计算,然后将中间结果存储在dp表,循环迭代

3. 动态规划优化

// 动态规划优化版斐波那契
function Fun(n){
	let f1 = 1
    let f2 = 1
    let sum = 1
    for (let i = 2; i<n; ++i){
    	sum = f1+f2
        f1 = f2
        f2 = sum
    }
    return sum
}
console.log(Fun(4))

动态规划优化版版最优空间,因为只用到了3个变量f1、f2和sum存储;因为没必要存储一整张表,每次迭代只用到f1和f2两个变量。并且也是最优时间复杂度O(n)循环;采用自底向上的思想,给了底部初始值,通过for循环进行迭代,求出最顶部的F(n)的值。

参考系列

labuladong.gitbook.io/algo/di-lin…

mp.weixin.qq.com/s/pg-IJ8rA1…