Merge pull request #1831 from juguagua/leetcode-modify-the-code-of-the-dp

更新动态规划部分:从 “单词拆分” 到 “买卖股票的最佳时机III”
This commit is contained in:
程序员Carl
2022-12-25 12:14:22 +08:00
committed by GitHub
7 changed files with 117 additions and 226 deletions

View File

@ -89,7 +89,7 @@ dp[i][1] 表示第i天不持有股票所得最多现金
**注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态** **注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态**
很多同学把“持有”和“买入”没区分清楚。 很多同学把“持有”和“买入”没区分清楚。
在下面递推公式分析中,我会进一步讲解。 在下面递推公式分析中,我会进一步讲解。
@ -103,11 +103,11 @@ dp[i][1] 表示第i天不持有股票所得最多现金
如果第i天不持有股票即dp[i][1] 也可以由两个状态推出来 如果第i天不持有股票即dp[i][1] 也可以由两个状态推出来
* 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1] * 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1]
* 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0] * 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0]
同样dp[i][1]取最大的dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); 同样dp[i][1]取最大的dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
这样递公式我们就分析完了 这样递公式我们就分析完了
3. dp数组如何初始化 3. dp数组如何初始化
@ -121,7 +121,7 @@ dp[0][1]表示第0天不持有股票不持有股票那么现金就是0
4. 确定遍历顺序 4. 确定遍历顺序
从递推公式可以看出dp[i]都是dp[i - 1]推导出来的,那么一定是从前向后遍历。 从递推公式可以看出dp[i]都是dp[i - 1]推导出来的,那么一定是从前向后遍历。
5. 举例推导dp数组 5. 举例推导dp数组
@ -326,53 +326,40 @@ Go:
> 贪心法: > 贪心法:
```Go ```Go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
low := math.MaxInt32 min := prices[0]
rlt := 0 res := 0
for i := range prices{ for i := 1; i < len(prices); i++ {
low = min(low, prices[i]) if prices[i] - min > res {
rlt = max(rlt, prices[i]-low) res = prices[i]-min
} }
if min > prices[i] {
return rlt min = prices[i]
}
func min(a, b int) int {
if a < b{
return a
} }
return b
}
func max(a, b int) int {
if a > b{
return a
} }
return res
return b
} }
``` ```
> 动态规划:版本一 > 动态规划:版本一
```Go ```Go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
length:=len(prices) length := len(prices)
if length==0{return 0} if length == 0{return 0}
dp:=make([][]int,length) dp := make([][]int,length)
for i:=0;i<length;i++{ for i := 0; i < length; i++ {
dp[i]=make([]int,2) dp[i] = make([]int, 2)
} }
dp[0][0] = -prices[0]
dp[0][0]=-prices[0] dp[0][1] = 0
dp[0][1]=0 for i := 1; i < length; i++ {
for i:=1;i<length;i++{ dp[i][0] = max(dp[i-1][0], -prices[i])
dp[i][0]=max(dp[i-1][0],-prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
} }
return dp[length-1][1] return dp[length-1][1]
} }
func max(a,b int)int { func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b
@ -385,7 +372,7 @@ func maxProfit(prices []int) int {
dp := [2][2]int{} dp := [2][2]int{}
dp[0][0] = -prices[0] dp[0][0] = -prices[0]
dp[0][1] = 0 dp[0][1] = 0
for i := 1; i < len(prices); i++{ for i := 1; i < len(prices); i++ {
dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i]) dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i])
dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i]) dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i])
} }

View File

@ -39,7 +39,7 @@
本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。 本题我们在讲解贪心专题的时候就已经讲解过了[贪心算法买卖股票的最佳时机II](https://programmercarl.com/0122.买卖股票的最佳时机II.html),只不过没有深入讲解动态规划的解法,那么这次我们再好好分析一下动规的解法。
本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票) 本题和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)
**在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的** **在动规五部曲中,这个区别主要是体现在递推公式上,其他都和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)一样一样的**
@ -63,9 +63,9 @@
那么第i天持有股票即dp[i][0]如果是第i天买入股票所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即dp[i - 1][1] - prices[i]。 那么第i天持有股票即dp[i][0]如果是第i天买入股票所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即dp[i - 1][1] - prices[i]。
来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来 来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来
* 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1] * 第i-1天就不持有股票那么就保持现状所得现金就是昨天不持有股票的所得现金 即dp[i - 1][1]
* 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0] * 第i天卖出股票所得现金就是按照今天股票价格卖出后所得现金即prices[i] + dp[i - 1][0]
**注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!** **注意这里和[121. 买卖股票的最佳时机](https://programmercarl.com/0121.买卖股票的最佳时机.html)就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!**
@ -99,7 +99,7 @@ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
**这正是因为本题的股票可以买卖多次!** 所以买入股票的时候可能会有之前买卖的利润即dp[i - 1][1]所以dp[i - 1][1] - prices[i]。 **这正是因为本题的股票可以买卖多次!** 所以买入股票的时候可能会有之前买卖的利润即dp[i - 1][1]所以dp[i - 1][1] - prices[i]。
想到到这一点,对这两道题理解的比较深刻了。 想到到这一点,对这两道题理解的比较深刻了。
这里我依然给出滚动数组的版本C++代码如下: 这里我依然给出滚动数组的版本C++代码如下:
@ -228,29 +228,6 @@ func max(a, b int) int {
} }
``` ```
```go
func maxProfit(prices []int) int {
//创建数组
dp:=make([][]int,len(prices))
for i:=0;i<len(prices);i++{
dp[i]=make([]int,2)
}
dp[0][0]=-prices[0]
dp[0][1]=0
for i:=1;i<len(prices);i++{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
}
return dp[len(prices)-1][1]
}
func max(a,b int)int{
if a<b{
return b
}
return a
}
```
Javascript Javascript
```javascript ```javascript
// 方法一动态规划dp 数组) // 方法一动态规划dp 数组)

View File

@ -62,7 +62,7 @@ dp[i][j]中 i表示第i天j为 [0 - 4] 五个状态dp[i][j]表示第i天
需要注意dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。 需要注意dp[i][1]**表示的是第i天买入股票的状态并不是说一定要第i天买入股票这是很多同学容易陷入的误区**。
例如 dp[i][1] ,并不是说 第i一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。 例如 dp[i][1] ,并不是说 第i一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。
2. 确定递推公式 2. 确定递推公式
@ -102,7 +102,7 @@ dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
第0天第二次买入操作初始值应该是多少呢应该不少同学疑惑第一次还没买入呢怎么初始化第二次买入呢 第0天第二次买入操作初始值应该是多少呢应该不少同学疑惑第一次还没买入呢怎么初始化第二次买入呢
第二次买入依赖于第一次卖出的状态其实相当于第0天第一次买入了第一次卖出了然后买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。 第二次买入依赖于第一次卖出的状态其实相当于第0天第一次买入了第一次卖出了然后买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。
所以第二次买入操作初始化为dp[0][3] = -prices[0]; 所以第二次买入操作初始化为dp[0][3] = -prices[0];
@ -181,7 +181,7 @@ public:
dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。 dp[1] = max(dp[1], dp[0] - prices[i]); 如果dp[1]取dp[1],即保持买入股票的状态,那么 dp[2] = max(dp[2], dp[1] + prices[i]);中dp[1] + prices[i] 就是今天卖出。
如果dp[1]取dp[0] - prices[i]今天买入股票那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是尽在再卖出股票一买一卖收益为0对所得现金没有影响。相当于今天买入股票又卖出股票等于没有操作保持昨天卖出股票的状态了。 如果dp[1]取dp[0] - prices[i]今天买入股票那么dp[2] = max(dp[2], dp[1] + prices[i]);中的dp[1] + prices[i]相当于是今天再卖出股票一买一卖收益为0对所得现金没有影响。相当于今天买入股票又卖出股票等于没有操作保持昨天卖出股票的状态了。
**这种写法看上去简单,其实思路很绕,不建议大家这么写,这么思考,很容易把自己绕进去!** **这种写法看上去简单,其实思路很绕,不建议大家这么写,这么思考,很容易把自己绕进去!**
@ -312,26 +312,26 @@ Go:
```go ```go
func maxProfit(prices []int) int { func maxProfit(prices []int) int {
dp:=make([][]int,len(prices)) dp := make([][]int, len(prices))
for i:=0;i<len(prices);i++{ for i := 0; i < len(prices); i++ {
dp[i]=make([]int,5) dp[i] = make([]int, 5)
} }
dp[0][0]=0 dp[0][0] = 0
dp[0][1]=-prices[0] dp[0][1] = -prices[0]
dp[0][2]=0 dp[0][2] = 0
dp[0][3]=-prices[0] dp[0][3] = -prices[0]
dp[0][4]=0 dp[0][4] = 0
for i:=1;i<len(prices);i++{ for i := 1; i < len(prices); i++ {
dp[i][0]=dp[i-1][0] dp[i][0] = dp[i-1][0]
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]) dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]) dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i])
dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i]) dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i])
dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]) dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i])
} }
return dp[len(prices)-1][4] return dp[len(prices)-1][4]
} }
func max(a,b int)int{ func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b
@ -407,39 +407,6 @@ function maxProfit(prices: number[]): number {
}; };
``` ```
Go:
> 版本一:
```go
// 买卖股票的最佳时机III 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func maxProfit(prices []int) int {
dp := make([][]int, len(prices))
status := make([]int, len(prices) * 4)
for i := range dp {
dp[i] = status[:4]
status = status[4:]
}
dp[0][0], dp[0][2] = -prices[0], -prices[0]
for i := 1; i < len(prices); i++ {
dp[i][0] = max(dp[i - 1][0], -prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i])
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] - prices[i])
dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] + prices[i])
}
return dp[len(prices) - 1][3]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```

View File

@ -138,7 +138,7 @@ public:
3. dp数组如何初始化 3. dp数组如何初始化
从递公式中可以看出dp[i] 的状态依靠 dp[j]是否为true那么dp[0]就是递的根基dp[0]一定要为true否则递下去后面都都是false了 从递公式中可以看出dp[i] 的状态依靠 dp[j]是否为true那么dp[0]就是递的根基dp[0]一定要为true否则递下去后面都都是false了
那么dp[0]有没有意义呢 那么dp[0]有没有意义呢
@ -152,13 +152,13 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
题目中说是拆分为一个或多个在字典中出现的单词所以这是完全背包 题目中说是拆分为一个或多个在字典中出现的单词所以这是完全背包
还要讨论两层for循环的前后 还要讨论两层for循环的前后
**如果求组合数就是外层for循环遍历物品内层for遍历背包** **如果求组合数就是外层for循环遍历物品内层for遍历背包**
**如果求排列数就是外层for遍历背包内层for循环遍历物品** **如果求排列数就是外层for遍历背包内层for循环遍历物品**
我在这里做一个一个总结 我在这里做一个总结
求组合数[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html) 求组合数[动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)
求排列数[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html) 求排列数[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和.html)[动态规划70. 爬楼梯进阶版(完全背包)](https://programmercarl.com/0070.爬楼梯完全背包版本.html)
@ -170,7 +170,7 @@ dp[0]表示如果字符串为空的话,说明出现在字典里。
"apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的那么我们就是强调物品之间顺序 "apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的那么我们就是强调物品之间顺序
所以说本题一定是 先遍历 背包遍历物品 所以说本题一定是 先遍历 背包遍历物品
5. 举例推导dp[i] 5. 举例推导dp[i]
@ -209,7 +209,7 @@ public:
关于遍历顺序再给大家讲一下为什么 先遍历物品再遍历背包不行 关于遍历顺序再给大家讲一下为什么 先遍历物品再遍历背包不行
这里可以给出先遍历物品遍历背包的代码 这里可以给出先遍历物品遍历背包的代码
```CPP ```CPP
class Solution { class Solution {
@ -241,7 +241,7 @@ public:
最后dp[s.size()] = 0 dp[13] = 0 ,而不是1,因为先用 "apple" 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1 最后dp[s.size()] = 0 dp[13] = 0 ,而不是1,因为先用 "apple" 去遍历的时候,dp[8]并没有被赋值为1 (还没用"pen"),所以 dp[13]也不能变成1
除非是先用 "apple" 遍历一遍, "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1 除非是先用 "apple" 遍历一遍, "pen" 遍历,此时 dp[8]已经是1,最后再用 "apple" 去遍历,dp[13]才能是1
如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。 如果大家对这里不理解,建议可以把我上面给的代码,拿去力扣上跑一跑,把dp数组打印出来,对着递推公式一步一步去看,思路就清晰了。
@ -352,16 +352,16 @@ class Solution:
Go Go
```Go ```Go
func wordBreak(s string,wordDict []string) bool { func wordBreak(s string,wordDict []string) bool {
wordDictSet:=make(map[string]bool) wordDictSet := make(map[string]bool)
for _,w:=range wordDict{ for _, w := range wordDict {
wordDictSet[w]=true wordDictSet[w] = true
} }
dp:=make([]bool,len(s)+1) dp := make([]bool, len(s)+1)
dp[0]=true dp[0] = true
for i:=1;i<=len(s);i++{ for i := 1; i <= len(s); i++ {
for j:=0;j<i;j++{ for j := 0; j < i; j++ {
if dp[j]&& wordDictSet[s[j:i]]{ if dp[j] && wordDictSet[s[j:i]] {
dp[i]=true dp[i] = true
break break
} }
} }

View File

@ -52,7 +52,7 @@
如果偷第i房间那么dp[i] = dp[i - 2] + nums[i] 第i-1房一定是不考虑的找出 下标i-2包括i-2以内的房屋最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。 如果偷第i房间那么dp[i] = dp[i - 2] + nums[i] 第i-1房一定是不考虑的找出 下标i-2包括i-2以内的房屋最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
如果不偷第i房间那么dp[i] = dp[i - 1]即考虑i-1房**注意这里是考虑并不是一定要偷i-1房这是很多同学容易混淆的点** 如果不偷第i房间那么dp[i] = dp[i - 1],即考 虑i-1房**注意这里是考虑并不是一定要偷i-1房这是很多同学容易混淆的点**
然后dp[i]取最大值即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); 然后dp[i]取最大值即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
@ -154,26 +154,17 @@ class Solution:
Go Go
```Go ```Go
func rob(nums []int) int { func rob(nums []int) int {
if len(nums)<1{ n := len(nums)
return 0 dp := make([]int, n+1) // dp[i]表示偷到第i家能够偷得的最大金额
dp[1] = nums[0]
for i := 2; i <= n; i++ {
dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
} }
if len(nums)==1{ return dp[n]
return nums[0]
}
if len(nums)==2{
return max(nums[0],nums[1])
}
dp :=make([]int,len(nums))
dp[0]=nums[0]
dp[1]=max(nums[0],nums[1])
for i:=2;i<len(nums);i++{
dp[i]=max(dp[i-2]+nums[i],dp[i-1])
}
return dp[len(dp)-1]
} }
func max(a, b int) int { func max(a, b int) int {
if a>b{ if a > b {
return a return a
} }
return b return b

View File

@ -143,6 +143,44 @@ class Solution:
return dp[-1] return dp[-1]
``` ```
Go
```go
// 打家劫舍Ⅱ 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func rob(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
if len(nums) == 2 {
return max(nums[0], nums[1])
}
result1 := robRange(nums, 0)
result2 := robRange(nums, 1)
return max(result1, result2)
}
// 偷盗指定的范围
func robRange(nums []int, start int) int {
dp := make([]int, len(nums))
dp[1] = nums[start]
for i := 2; i < len(nums); i++ {
dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1])
}
return dp[len(nums) - 1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
javascipt: javascipt:
```javascript ```javascript
var rob = function(nums) { var rob = function(nums) {
@ -187,44 +225,6 @@ function robRange(nums: number[], start: number, end: number): number {
} }
``` ```
Go
```go
// 打家劫舍Ⅱ 动态规划
// 时间复杂度O(n) 空间复杂度O(n)
func rob(nums []int) int {
if len(nums) == 1 {
return nums[0]
}
if len(nums) == 2 {
return max(nums[0], nums[1])
}
result1 := robRange(nums, 0)
result2 := robRange(nums, 1)
return max(result1, result2)
}
// 偷盗指定的范围
func robRange(nums []int, start int) int {
dp := make([]int, len(nums))
dp[1] = nums[start]
for i := 2; i < len(nums); i++ {
dp[i] = max(dp[i - 2] + nums[i - 1 + start], dp[i - 1])
}
return dp[len(nums) - 1]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
<p align="center"> <p align="center">

View File

@ -129,7 +129,7 @@ if (cur == NULL) return vector<int>{0, 0};
3. 确定遍历顺序 3. 确定遍历顺序
首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。 首先明确的是使用后序遍历。 因为通过递归函数的返回值来做下一步计算。
通过递归左节点,得到左节点偷与不偷的金钱。 通过递归左节点,得到左节点偷与不偷的金钱。
@ -147,7 +147,7 @@ vector<int> right = robTree(cur->right); // 右
4. 确定单层递归的逻辑 4. 确定单层递归的逻辑
如果是偷当前节点那么左右孩子就不能偷val1 = cur->val + left[0] + right[0]; **如果对下标含义不理解就回顾一下dp数组的含义** 如果是偷当前节点那么左右孩子就不能偷val1 = cur->val + left[0] + right[0]; **如果对下标含义不理解就回顾一下dp数组的含义**
如果不偷当前节点那么左右孩子就可以偷至于到底偷不偷一定是选一个最大的所以val2 = max(left[0], left[1]) + max(right[0], right[1]); 如果不偷当前节点那么左右孩子就可以偷至于到底偷不偷一定是选一个最大的所以val2 = max(left[0], left[1]) + max(right[0], right[1]);
@ -483,37 +483,6 @@ function robNode(node: TreeNode | null): MaxValueArr {
} }
``` ```
### Go
```go
// 打家劫舍Ⅲ 动态规划
// 时间复杂度O(n) 空间复杂度O(logn)
func rob(root *TreeNode) int {
dp := traversal(root)
return max(dp[0], dp[1])
}
func traversal(cur *TreeNode) []int {
if cur == nil {
return []int{0, 0}
}
dpL := traversal(cur.Left)
dpR := traversal(cur.Right)
val1 := cur.Val + dpL[0] + dpR[0] // 偷盗当前节点
val2 := max(dpL[0], dpL[1]) + max(dpR[0], dpR[1]) // 不偷盗当前节点
return []int{val2, val1}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
```
<p align="center"> <p align="center">