持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天 本文先通过斐波那契数,比对递归法和动态规划法的不同,来初步理解动态规划。然后再加上一个零钱兑换加深思路
力扣509-斐波那契数
本例会用四种方法来实现:递归法,动态规划法,备忘录法,两数法。
1.递归法
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if (n === 0 || n === 1) {
return n;
}
return fib(n - 1) + fib(n - 2)
};
这种解法就是根据题意,直接书写,性能很差,但是面试如果出现(几乎不太可能),这个解法大概率是个负分答案了,所以下面进行优化。
2.动态规划法(推荐)
解题思路:动态规划(dynamic programming)可以理解为递推。
我们先定义一个初始化条件,然后写递推公式,最后返回结果。
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
let dp = [0,1]
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2]
}
return dp[n]
};
3.备忘录法
思路解释:先把斐波那契数列想象成一颗树,如下图所示
该结构中存在大量重复运算,那么我们把这部分缓存起来,存在则直接使用,不存在再进行缓存,最终得到结果。
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
let memo = []
function helper(memo, n) {
if (n <= 1) {
return n;
}
if (memo[n]) {
return memo[n]
}
memo[n] = helper(memo, n-1) + helper(memo, n-2)
return memo[n]
}
return helper(memo, n)
};
4.两数法
思路解析:f(n) = f(n - 1) + f(n - 2),f(n - 1)和f(n - 2)是两个数字,可以不用数组来保存,所以我们一直模拟两个数字来存储可以提升空间复杂度。空间复杂度O(n)变为O(1)
/**
* @param {number} n
* @return {number}
*/
var fib = function(n) {
if (n <= 1) {
return n
}
let dp1 = 0;
let dp2 = 1;
let dp3;
for (let i = 2; i <= n; i++) {
dp3 = dp1 + dp2;
dp1 = dp2;
dp2 = dp3;
}
return dp3;
};
力扣322-零钱兑换
本例不能用贪心算法,比如,coins=[1,2,5],amount=12,那么贪心算法不能回退,无法得到最优解。
- 以case1为例先定义无穷大的一组数组
- 第一次循环时,硬币为1的情况,那么每次累加1就可以完成,dp[金额-1]+1
- 第二次循环时,硬币为2的情况,j从2开始计数(j = coins[i]),否则不够找零没有计算的意义。amount为2时,可以对硬币2找零1次,所以计数1
amount为3时,可以对硬币2找零1次,但是还有对1找零1次,所以计数2
amount为4时,可以对硬币2找零2次(在红框的基础上再找1次)所以计数2
amount为5时,5-2=3,刚才我们在分析了3需要招零2次,那么再找零一次2就可以,所以为3;
amount为6时,6-2=4,那么在4的基础上+1,也为3,后面以此类推 - 第三次循环,amount从5开始找零,那么5就为1;
amount为6时,6-5=1,那我们在1的基础上加上刚才找零的5的个数1,所以为2;
amount为7时,7-5=2,此时2的位置也是1,所以加上找零的5的个数1,也为2;
amount为8时,7-5=3,3的位置是2(说明3需要最少两张),所以再加上刚才的5的一张,8的位置就是3。。。最后,3就是最小的张数
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
if (!amount) {
return 0;
}
let dp = Array(amount + 1).fill(Infinity);
dp[0] = 0;
for(let i = 0; i < coins.length; i++) {
for (let j = coins[i]; j <= amount; j++) {
dp[j] = Math.min(dp[j-coins[i]]+1, dp[j])
console.log(dp)
}
}
return dp[amount] === Infinity ? -1 : dp[amount]
};