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