【LeetCode】LeetCode之打家劫舍【暴力递归、动态规划、动态规划之优化空间的具体分析与实现】
🔒198打家劫舍:https://leetcode-cn.com/problems/house-robber/
🎃你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
給定一個代表每個房屋存放金額的非負整數數組,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額。
示例 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 。
提示:
- 1 <= nums.length <= 100
- 0 <= nums[i] <= 400
📕1.暴力解法-遞歸實現
使用遞歸解題首先想到的肯定是如何推導出遞推公式,也就是如何分解成一個個的子問題。如果你在腦子里遞來遞去的,那樣就陷入了一個誤區,很容易思維混亂。遞歸最最重要的是推導出遞推公式即可,至于怎么遞來遞去的交給計算機。
🧿(1)當nums.length=1時,那肯定最高金額就是nums[0]
🧿(2)當nums.lenght=2時,最高金額要么是nums[0]要么是nums[1]
🧿(3)當nums.length=3時,此時最高金額要么是nums[0]+nums[2]要么是nums[1],也就是max(nums[0]+nums[2],nums[1]);
…
🧿(4)當nums.length=n時,此時最高金額就是max(nums[n-1],nums[n-2]+nums[n]);
你會發現當我們求nums.length=n的最大金額時相當于對上述過程進行倒推,此時遞推公式就出來了。
遞歸中第二個比較重要的是找出終止條件
🛑由于我們遞推公式里包含nums[n-1]與nums[n-2],所以為了防止數組越界,終止條件就是當n==1 || n ==2時。
代碼實現:
public static int rob(int[] nums) { if (nums.length == 1) {//如果length=1,那么最大值肯定就是nums[0]return nums[0];}//由于我們的終止條件是當n==0||n==1時//當遞歸從n到1時就要回溯了,此時就得考慮當n=1時,nums[1]是最大金額而不是nums[0],所以有種情況對應下面的解釋nums[1] = Math.max(nums[0], nums[1]);return recursion(nums.length - 1, nums);}public static int recursion(int n, int[] nums) {if (n == 0 || n == 1) {return nums[n];//這里終止條件直接返回的是nums[n],代表此時n的最大金額//如果n=1,那么你能否確定nums[1]是最大金額而不是nums[0]呢//我們在rob方法體內要進行判斷,如果nums[0]>nums[1],要將nums[0]賦值給nums[1]}return Math.max(recursion(n - 1, nums), recursion(n - 2, nums) + nums[n]);}
從上圖我們可以看出使用遞歸出現了大量的重復計算(我只是枚舉前三行,越往后重復越多),這是遞歸比較忌諱的問題之一(重復和棧溢出)。這樣無異于在浪費CPU的運算單元。
復雜度分析:
空間復雜度:O(n)【每個棧空間復雜度為O(1)】O(n*1)=O(n)
時間復雜度:O(n^n)
- 遞歸算法的空間復雜度與所生成的最大遞歸樹的深度成正比。如果遞歸算法的每個函數調用都占用 O(m) 空間,并且如果遞歸樹的最大深度為 n,則遞歸算法的空間復雜度將為 O(n·m)。”
當我使用暴力解法提交到LeetCode時,可以看出超時了,這種方式解題肯定不行。
💌上述中遞歸解題方式的缺點就是重復量比較多,所以我們可以可以將途中計算的結果保存下來,俗稱記憶集或記憶化搜索。此時你應該想到這不正是動態規劃嘛。
2.記憶化搜索-動態規劃
如果你懂得了上述中的遞歸,那么理解動態規劃解題應該比較容易,首先要根據動態規劃的解題步驟進行分析
(1)分解為子問題,這個上述遞歸已經分析過
(2)確定狀態
(3)推導轉移方程,這個上述遞歸的遞推式
(4)尋找邊界條件,也就是上述遞歸中的終止條件
上述四個步驟,除了第二個我們都已經在上述遞歸中分析出來了,這個確定狀態也就是用什么來保存遍歷過程中的值,此題當然最適合使用一維數組進行保存
【比如dp[0]代表n=1時的最大金額;dp[1]代表n=2時的最大金額;dp[2]代表n=3時的最大金額,也就是dp[2]=max(dp[0]+nums[2],dp[1]),可以看出max中的dp[0]與dp[1]已經被記錄下來了。那么一旦到dp[n]它的最大金額就可以根據前面的最優結果進行求解。】
動態規劃一般都是通過迭代完成的,而不是遞歸。動態規劃=迭代+記憶集
🎩代碼實現
public static int rob(int[] nums) {if (nums.length == 1) {return nums[0];}int[] dp = new int[nums.length];//創建一個dp數組dp[0] = nums[0];//0和1都要先確定下來,因為下面遍歷時包含i-2dp[1] = Math.max(nums[0], nums[1]);for (int i = 2; i < nums.length; i++) {dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);}return dp[nums.length - 1];}復雜度分析
空間復雜度為O(n)
時間復雜度為O(n)
當我提交LeetCode上時,可以發現通過了。
分析一下:能不能將空間復雜度優化成O(1)呢?
3.動態規劃之優化空間復雜度
優化成O(1),那肯肯定就是使用0維數組作為記憶集了,經分析會發現每個結果只會被調用一次而且是有順序性的。
比如遍歷到i=4時,求maxMoney=max(nums[i-1],nums[i-2]+nums[i]);當用完一次nums[i-1]或者nums[i-2]時后續就不會再使用
使用pre保存當n-2時的最大金額
使用suf保存當n-1時的最大金額
復雜度分析
時間復雜度:O(n)
空間復雜度:O(1)
提交到LeetCode,當然能通過
總結
以上是生活随笔為你收集整理的【LeetCode】LeetCode之打家劫舍【暴力递归、动态规划、动态规划之优化空间的具体分析与实现】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【多线程高并发】深入浅出volatile
- 下一篇: 【LeetCode】LeetCode之打