日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【LeetCode】LeetCode之打家劫舍Ⅱ——暴力递归+动态规划解决循环问题+DP空间优化

發布時間:2024/10/5 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【LeetCode】LeetCode之打家劫舍Ⅱ——暴力递归+动态规划解决循环问题+DP空间优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這道題和第 198 題相似,建議讀者首先閱讀「198. 打家劫舍」

🔒LeetCode之打家劫舍ⅠLeetCode之打家劫舍Ⅰ


1.打家劫舍II 題目描述

你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金。這個地方所有的房屋都 圍成一圈 ,這意味著第一個房屋和最后一個房屋是緊挨著的。同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警 。

給定一個代表每個房屋存放金額的非負整數數組,計算你 在不觸動警報裝置的情況下 ,今晚能夠偷竊到的最高金額

💎示例 1:

輸入:nums = [2,3,2]
輸出:3
解釋:你不能先偷竊 1 號房屋(金額 = 2),然后偷竊 3 號房屋(金額 = 2), 因為他們是相鄰的。

💎示例 2:

輸入:nums = [1,2,3,1]
輸出:4
解釋:你可以先偷竊 1 號房屋(金額 = 1),然后偷竊 3 號房屋(金額 = 3)。
偷竊到的最高金額 = 1 + 3 = 4 。

💎示例 3:

輸入:nums = [0]
輸出:0

📜提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

2.打家劫舍Ⅱ思路分析

🔗直達打家劫舍①鏈接,默認讀者已經搞懂了打家劫舍①.

該問題是在打家劫舍①的基礎上,添加了一個循環結構。如果你已經理解了打家劫舍①,那么這個Ⅱ只需要考慮怎么解決這個循環問題即可,也就是第一個數據和最后一個數據不能同時出現,要么最大金額只包含第一個數據,要么只包含最后一個數據。

所以我們可以將數組分為兩部分,各自找出每一部分的最大金額,然后返回它們兩個金額最大的即可【你會發現這樣分為兩部分之后,就不會出現第一個和最后一個數據同時出現的問題了;然后每一部分的解題過程就和打家劫舍1一模一樣了。


3.暴力解法-遞歸實現

📕流程

(1)將數組分為兩部分即可

  • 0 ~ n - 1
  • 1 ~ n

(2)然后將每一部分按照打家劫舍Ⅰ求解即可
(3)返回這兩部分結果的最大值

public int rob(int[] nums) {int l = nums.length;if (l == 1) {return nums[0];}if (l == 2) {return Math.max(nums[0], nums[1]);}int[] preNums = new int[l - 1];int[] sufNums = new int[l - 1];for (int i = 0; i < l - 1; i++) {preNums[i] = nums[i];if (i > 0) {sufNums[i - 1] = nums[i];}}sufNums[l - 2] = nums[l - 1];//以上代碼是將數組分為兩個子數組【1~n,0~n-1】preNums[1] = Math.max(preNums[0], preNums[1]);sufNums[1] = Math.max(sufNums[0], sufNums[1]);//以上兩行與打家劫舍1類似return Math.max(recursion(preNums.length - 1, preNums), recursion(sufNums.length - 1, sufNums));}public int recursion(int n, int[] nums) {if (n == 0 || n == 1) {return nums[n];}return Math.max(recursion(n - 1, nums), recursion(n - 2, nums) + nums[n]);}

當我將該暴力解法提交到LeetCode,發現會超時。


從上圖我們可以看出使用遞歸出現了大量的重復計算(我只是枚舉前三行,越往后重復越多),這是遞歸比較忌諱的問題之一(重復和棧溢出)。這樣無異于在浪費CPU的運算單元。

復雜度分析:
空間復雜度:O(n)【每個棧空間復雜度為O(1)】O(n*1)=O(n)
時間復雜度:O(n^n)

  • 遞歸算法的空間復雜度與所生成的最大遞歸樹的深度成正比。如果遞歸算法的每個函數調用都占用 O(m) 空間,并且如果遞歸樹的最大深度為 n,則遞歸算法的空間復雜度將為 O(n·m)。”

💌上述中遞歸解題方式的缺點就是重復量比較多,所以我們可以可以將途中計算的結果保存下來,俗稱記憶集或記憶化搜索。此時你應該想到這不正是動態規劃嘛。


4.記憶化搜索-動態規劃

需要兩個dp數組進行保存,因為在抽象層面分為兩個數組了嘛,所以要使用兩個記憶集。其他就和打家劫舍1一樣了。

循環體內部包含兩部分

  • 0 ~ n-1
  • 1 ~ n
public int rob2(int[] nums) {if (nums.length == 1) {return nums[0];}if (nums.length == 2) {return Math.max(nums[0], nums[1]);}int[] dp1 = new int[nums.length - 1];dp1[0] = nums[0];dp1[1] = Math.max(nums[0], nums[1]);int[] dp2 = new int[nums.length - 1];dp2[0] = nums[1];dp2[1] = Math.max(nums[1], nums[2]);for (int i = 2; i < nums.length - 1; i++) {//0~n-1dp1[i] = Math.max(dp1[i - 1], dp1[i - 2] + nums[i]);//1~ndp2[i] = Math.max(dp2[i - 1], dp2[i - 2] + nums[i + 1]);}return Math.max(dp1[nums.length - 2], dp2[nums.length - 2]);}

復雜度分析

空間復雜度為O(n)
時間復雜度為O(n)

當我提交LeetCode上時,可以發現通過了。
分析一下:能不能將空間復雜度優化成O(1)呢?


5.動態規劃之優化空間復雜度

如果理解了打家劫舍1和前面的流程,這個應該很簡單。

public int rob3(int[] nums) {if (nums.length == 1) {return nums[0];}if (nums.length == 2) {return Math.max(nums[0], nums[1]);}int pre1 = nums[0], pre2 = nums[1];int suf1 = Math.max(nums[0], nums[1]);int suf2 = Math.max(nums[1], nums[2]);int maxMoney1 = suf1;int maxMoney2 = suf2;for (int i = 2; i < nums.length - 1; i++) {maxMoney1 = Math.max(pre1 + nums[i], suf1);pre1 = suf1;suf1 = maxMoney1;maxMoney2 = Math.max(pre2 + nums[i + 1], suf2);pre2 = suf2;suf2 = maxMoney2;}return Math.max(maxMoney1, maxMoney2);}

復雜度分析

時間復雜度:O(n)
空間復雜度:O(1)

提交到LeetCode,當然能通過


總結

以上是生活随笔為你收集整理的【LeetCode】LeetCode之打家劫舍Ⅱ——暴力递归+动态规划解决循环问题+DP空间优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。