给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
/**
* @param {number[]} nums
* @return {number}
*/
// Solution One -- 68ms 34.2mb
var rob0 = function(nums) {
let len = nums.length
let a = b = 0
for(let i = 0; i < len; i++){
let c = Math.max(a, b + nums[i])
b = a
a = c
}
return a
};
// Solution Two-- 68ms 34.2mb
// core:dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i])
var rob1 = function(nums) {
let len = nums.length
if(len === 0) return 0
else if(len === 1) return nums[0]
let arr = new Array(len+1).fill(0)
arr[0] = nums[0]
arr[1] = Math.max(nums[0], nums[1])
for(let i = 2; i < len; i++){
arr[i] = Math.max(arr[i-1], arr[i-2] + nums[i])
}
return arr[len - 1]
};
// Solution Three -- 68ms 33.5mb
var rob2 = function(nums) {
let len = nums.length
if(len === 0) return 0
let dp0 = 0, dp1 = nums[0]
for(let i = 1; i < len; i++){
let tdp0 = Math.max(dp0, dp1),
tdp1 = dp0 + nums[i]
dp0 = tdp0
dp1 = tdp1
}
return Math.max(dp0, dp1)
};
// one和three是一致的思路
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
// 68ms 34.8mb
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
let len = nums.length
if(len === 0) return 0
else if(len === 1) return nums[0]
return Math.max(getRob(nums, 0, len-1), getRob(nums, 1, len))
};
var getRob = function(nums, start, end){
let dp0 = 0, dp1 = nums[start]
for(let i = start+1; i < end; i++){
let tdp0 = Math.max(dp0, dp1),
tdp1 = dp0 + nums[i]
dp0 = tdp0
dp1 = tdp1
}
return Math.max(dp0, dp1)
}
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:
输入: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
// Solution One -- 隔层偷,比较这层与下层偷到的最大值
// 2612ms 37.3mb
var rob0 = function(root) {
if(root === null) return 0
let money = root.val
if(root.left !== null){
money = money + rob(root.left.left) + rob(root.left.right)
}
if(root.right !== null){
money = money + rob(root.right.left) + rob(root.right.right)
}
return Math.max(money, rob(root.left) + rob(root.right))
};
// 以上思路,发现太慢,如果数量过大,可能就超时了。参考网友(leetcode账户名reals)思路得之,有以下优化
// Solution Two -- 存储好已计算过的根节点
// 84ms 38.2mb
var rob = function(root){
let nodeMoneyMap = new Map()
return robMoney1(root, nodeMoneyMap)
}
var robMoney1 = function(root, nodeMoneyMap){
if(root === null) return 0
if(nodeMoneyMap.has(root)) return nodeMoneyMap.get(root)
let money = root.val
if(root.left !== null){
money = money + robMoney1(root.left.left, nodeMoneyMap) + robMoney1(root.left.right, nodeMoneyMap)
}
if(root.right !== null){
money = money + robMoney1(root.right.left, nodeMoneyMap) + robMoney1(root.right.right, nodeMoneyMap)
}
let resMoney = Math.max(money, robMoney1(root.left, nodeMoneyMap) + robMoney1(root.right, nodeMoneyMap))
nodeMoneyMap.set(root, resMoney)
return resMoney
}
// Solution Three -- 根据网友(leetcode账户名reals),还有一种思路
/*
使用一个大小为2的数组来表示 int[] res = new int[2] 0代表不偷,1代表偷
任何一个节点能偷到的最大钱的状态可以定义为
当前节点选择不偷: 当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
当前节点选择偷: 当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
表示为公式如下
root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) + Math.max(rob(root.right)[0], rob(root.right)[1])
root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;
作者:reals
*/
// 84ms 38.9mb
var rob2 = function(root){
let rootRes = robMoney2(root)
return Math.max(rootRes[0], rootRes[1])
}
var robMoney2 = function(root){
if(root === null) return [0, 0]
let res = new Array(2).fill(0),
left = robMoney2(root.left),
right = robMoney2(root.right)
res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1])
res[1] = left[0] + right[0] + root.val
return res
}