LC 53. 最大子序和
阿新 • • 發佈:2021-09-28
今天在 DSA 課上提到了這道題,我就搜來做了一下。
題目描述
給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
暴力列舉什麼的思路就不提了,肯定超時了。
思路1:DP
這個題看到我的第一想法就是用 DP,用 f[j] 表示以 j 結尾的連續子序列的最大和,狀態轉移方程就是 \(f[j] = max(f[j-1]+nums[j],nums[j])\),然後最後再取所有 f[j] 的最大值即得到答案。
class Solution { public: int f[300010]; int maxSubArray(vector<int>& nums) { int n = nums.size(); f[0] = nums[0]; for(int i = 1; i < n; i ++){ f[i] = max(f[i-1]+nums[i],nums[i]); } int maxSum = f[0]; for(int i = 1;i < n; i ++)maxSum = max(maxSum,f[i]); return maxSum; } };
可以看出 f[j] 只和 f[j-1] 有關係,因此其實不用開一個數組來存,只要用一個變數記錄以當前位置結尾的最大子序列的和以及到目前為止的所有子序列的最大和即可。
class Solution { public: int maxSubArray(vector<int>& nums) { int n = nums.size(); int curSum = nums[0]; int maxSum = nums[0]; for(int i = 1; i < n; i ++){ curSum = max(curSum+nums[i],nums[i]); maxSum = max(maxSum,curSum); } return maxSum; } };
顯然這種解法是 \(O(n)\) 的。
思路2:分治
考慮分治的思考方式:
- 分解:可以將問題分解為求解三個子問題:左半區間的最大子序和,右半區間的最大子序和,橫跨中點的最大子序和。
- 解決:左右半區間的子問題可以遞迴求解,需要解決的只有橫跨中點的最大子序和。
要求橫跨中點的最大子序和,只需要求左區間以中點為右端點的最大子序和和右區間以中點右邊第一個點為左端點的最大子序和相加即可。 - 合併:對三個子問題的解求 max 即可。
class Solution { public int findMaxSum(int[] nums,int l,int r){ if(l == r)return nums[l]; int mid = l+r>>1; int leftMaxSum = findMaxSum(nums,l,mid); int rightMaxSum = findMaxSum(nums,mid+1,r); int lSum = 0; int lMaxSum = nums[mid]; for(int t = mid; t >= l; t--){ lSum += nums[t]; lMaxSum = Math.max(lSum,lMaxSum); } int rSum = 0; int rMaxSum = nums[mid+1]; for(int t = mid+1; t <= r; t++){ rSum += nums[t]; rMaxSum = Math.max(rMaxSum,rSum); } int maxCrossSum = lMaxSum + rMaxSum; return Math.max(maxCrossSum,Math.max(leftMaxSum,rightMaxSum)); } public int maxSubArray(int[] nums) { return findMaxSum(nums,0,nums.length-1); } }
時間複雜度分析:可以寫出遞推式 \(T(n) = 2T(n/2)+\Theta(n)\),由主方法或者遞迴樹的方法都可以解得 \(T(n) = \Theta(nlgn)\)