题目
题目链接:leetcode-cn.com/leetbook/re…
题解
动态规划的分析步骤
1、划分边界,确定子问题
- 当只有一间房时,直接偷;
- 当有两间房时,偷金额高的;
- 当有三间房时,可以先偷第一间,因为第二间不能偷,就回到一间房的问题;或者直接偷第二间或者第三间(这就是两间房的问题),然后结束;
- 当有四间房时,可以先偷第一间,然后回到有两间房的问题;或者看成有三间房的问题;
2、定义优化函数,列出递推方程
设 f(n) 为长度为 n 的 nums 的一夜能偷到的最高金额;
由递推方程可知,此问题满足优化原则,可以使用动态规划;
1、递归实现
按照递推方程,写出递归的算法;
但是其时间和空间复杂度太高;
空间耗费来说,因为递归需要递归栈,并且每次传入的子数组都是使用 Array.prototype.slice()
方法生成的新数组,所以空间复杂度较大;
时间耗费来说,每次计算子问题都会重复计算另一个子问题已经计算过的问题,所以时间复杂度较大;
在 leetcode 编辑器中,以下算法对于规模小的问题来说可以正确得到结果,对于规模稍大的问题来说会报错超出时间限制;
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let len = nums.length;
if(len === 1) {
return nums[0];
}else if(len === 2) {
return Math.max(nums[0],nums[1]);
}
return Math.max(nums[len-1]+rob(nums.slice(0,len-2)),rob(nums.slice(0,len-1)))
};
复制代码
2、迭代实现
因为递归方法需要重复计算子问题,所以时间复杂度太大,这里使用迭代的方法,并使用一个数组 resultArr
记录每一次的子问题的解,以便减少时间复杂度;
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let len = nums.length;
if(len === 1) {
return nums[0];
}else if(len === 2) {
return Math.max(nums[0],nums[1]);
}
let resultArr = [nums[0],Math.max(nums[0],nums[1])]; // 用来存放各子问题的结果数组,以便减少重复计算;
for(let i = 2;i < len;i++) {
resultArr[i] = Math.max(nums[i] + resultArr[i-2],resultArr[i-1]);
}
return resultArr[len-1];
};
复制代码
观察上面代码的循环迭代部分,每次计算规模为 n 的问题只需要用到前面规模为 n-1 和 n-2 的问题;
因此并不需要用一个数组来记录子问题的解,只需要两个变量记录就可以了;
如下:
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let len = nums.length;
if(len === 1) {
return nums[0];
}else if(len === 2) {
return Math.max(nums[0],nums[1]);
}
let result1 = nums[0],
result2 = Math.max(nums[0],nums[1]); // 使用两个变量记录规模为 n-1 和 n-2 的子问题的解;
let temp = null;
for(let i = 2;i < len;i++) {
temp = Math.max(nums[i] + result1,result2);
result1 = result2;
result2 = temp;
}
return result2;
};
复制代码
大家如果有更好的思路和解法,欢迎大家一起来讨论啊~
这是使用 JavaScript 对 LeetCode《初级算法》的每道题的总结和实现的其中一篇,汇总篇在这里: