[Leetcode][第337题][JAVA][打家劫舍3][递归][动态规划]
【問題描述】[中等]
【解答思路】
1. 動態規劃
第 1 步:狀態定義
dp[node][j] :這里 node 表示一個結點,以 node 為根結點的樹,并且規定了 node 是否偷取能夠獲得的最大價值。
j = 0 表示 node 結點不偷取;
j = 1 表示 node 結點偷取。
第 2 步: 推導狀態轉移方程
根據當前結點偷或者不偷,就決定了需要從哪些子結點里的對應的狀態轉移過來。
如果當前結點不偷,左右子結點偷或者不偷都行,選最大者;
如果當前結點偷,左右子結點均不能偷。
(狀態轉移方程的表述有點復雜,請大家直接看代碼。)
第 3 步: 初始化
一個結點都沒有,空節點,返回 0,對應后序遍歷時候的遞歸終止條件;
第 4 步: 輸出
在根結點的時候,返回兩個狀態的較大者。
第 5 步: 思考優化空間
優化不了。
時間復雜度:O(N) 空間復雜度:O(N)
2. 遞歸
使用爺爺、兩個孩子、4 個孫子來說明問題
首先來定義這個問題的狀態
爺爺節點獲取到最大的偷取的錢數呢
根據以上條件,我們可以得出單個節點的錢該怎么算
4 個孫子偷的錢 + 爺爺的錢 VS 兩個兒子偷的錢 哪個組合錢多,就當做當前節點能偷的最大錢數。這就是動態規劃里面的最優子結構
由于是二叉樹,這里可以選擇計算所有子節點
4 個孫子投的錢加上爺爺的錢如下
int method1 = root.val + rob(root.left.left) + rob(root.left.right) + rob(root.right.left) + rob(root.right.right)
兩個兒子偷的錢如下
int method2 = rob(root.left) + rob(root.right);
挑選一個錢數多的方案則
int result = Math.max(method1, method2);
2.1暴力遞歸
public int rob(TreeNode root) {if (root == null) return 0;int money = root.val;if (root.left != null) {money += (rob(root.left.left) + rob(root.left.right));}if (root.right != null) {money += (rob(root.right.left) + rob(root.right.right));}return Math.max(money, rob(root.left) + rob(root.right)); }2.2 記憶化遞歸
針對2.1中速度太慢的問題,經過分析其實現,我們發現爺爺在計算自己能偷多少錢的時候,同時計算了 4 個孫子能偷多少錢,也計算了 2 個兒子能偷多少錢。這樣在兒子當爺爺時,就會產生重復計算一遍孫子節點。
于是乎我們發現了一個動態規劃的關鍵優化點
我們這一步針對重復子問題進行優化,我們在做斐波那契數列時,使用的優化方案是記憶化,但是之前的問題都是使用數組解決的,把每次計算的結果都存起來,下次如果再來計算,就從緩存中取,不再計算了,這樣就保證每個數字只計算一次。
由于二叉樹不適合拿數組當緩存,我們這次使用哈希表來存儲結果,TreeNode 當做 key,能偷的錢當做 value
【總結】
1. 動態規劃流程
第 1 步:設計狀態
第 2 步:狀態轉移方程
第 3 步:考慮初始化
第 4 步:考慮輸出
第 5 步:考慮是否可以狀態壓縮
2.遞歸 遞推公式要找準 后用記憶化搜索優化 這題不能用BFS,層次遍歷不符合題解
轉載鏈接:https://leetcode-cn.com/problems/house-robber-iii/solution/san-chong-fang-fa-jie-jue-shu-xing-dong-tai-gui-hu/
轉載鏈接:https://leetcode-cn.com/problems/house-robber-iii/solution/shu-xing-dp-ru-men-wen-ti-by-liweiwei1419/
總結
以上是生活随笔為你收集整理的[Leetcode][第337题][JAVA][打家劫舍3][递归][动态规划]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目标跟踪算法MOSSE笔记
- 下一篇: SQLite数据库常用语句及MAC上的S