leetcode 53. 最大子序和(分治法和動態規劃)
阿新 • • 發佈:2019-02-06
給定一個整數陣列 nums
,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:
如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。
思路:
程式碼:
動態規劃:很簡單,定義一個數組,dp[],dp[i]以第i個元素為結尾的一段最大子序和。求dp[i]時,假設前面dp[0]~dp[i-1]都已經求出來了,dp[i-1]表示的是以i-1為結尾的最大子序和,若dp[i-1]小於0,則dp[i]加上前面的任意長度的序列和都會小於不加前面的序列(即自己本身一個元素是以自己為結尾的最大自序和)。舉個例子:如-2,1,-3,4陣列,dp[0]=-2;dp[1]=1(因為前一個dp[0]=-2<0,即(-2,1)子序和為-1,一個元素(1)子序和為1);dp[2]=dp[1]+nums[2]=1+(-3)=-2;dp[3]=4,因為dp[2]<0;
class Solution { public: int maxSubArray(vector<int>& nums) { int len=nums.size(); if(len==0) return 0; if(len==1) return nums[0]; vector<int>dp(len,0); dp[0]=nums[0]; int max_num=dp[0]; int i=1; for(;i<len;i++){ if(dp[i-1]>0) dp[i]=dp[i-1]+nums[i]; else dp[i]=nums[i]; max_num=max(dp[i],max_num); } return max_num; } };
方法二、分治法:
思路:假設陣列下標有效範圍是l到r,將陣列分為左半部分下標為(l,mid-1)和右半部分下標為(mid+1,r)以及中間元素下標為mid,接下來遞迴求出左半部分的最大子序和:left=helper(nums,l,mid-1); 右半部分最大子序和right=helper(nums,mid+1,r);
接下來再將左半部分右邊界,右半部分左邊界以及中間元素nums[mid]整合,用了兩個迴圈,先整合左半部分右邊界和中間值,再將整合結果與右半部分左邊界整合得到整合以後的最大子序和max_num,最後返回max_num,left,right的最大值即是要求的最大子序和。
class Solution { public: int maxSubArray(vector<int>& nums) { if(nums.size()==0)return 0; return helper(nums,0,nums.size()-1); } int helper(vector<int>& nums,int l,int r){ if(l>r)return INT_MIN;//注意此處不是返回0,比如{-2,-1},分治以後變為左中右n{},-1,{-2}三部分。左半部分{}應返回INT_MIN, //因為還要和右半部分的返回值進行比較,最終正確結果返回-1。若左半部分返回0,0>-2,且大於左中右的最大組合值(-1),最終結果返回0,出錯 if(l==r)return nums[l]; int mid=(l+r)/2; int left=helper(nums,l,mid-1); int right=helper(nums,mid+1,r); int t=nums[mid]; int max_num=nums[mid]; for(int i=mid-1;i>=l;i--){ t+=nums[i]; max_num=max(max_num,t); } t=max_num; for(int i=mid+1;i<=r;i++){ t+=nums[i]; max_num=max(max_num,t); } return max(max(left,right),max_num); } };