Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
这道题让求数组中和最大的子数组的和,类似这种“问题的子问题的最优解”问题首先想到的是使用动态规划的方法求解,使用动态规划的难点在于使用适当的dp数组写出问题的正确的状态转移方程,本题的状态转移方程可以表示为:dp[i]=max(dp[i-1]+nums[i],nums[i]),其中dp[i]表示数组中以nums[i]结尾的和最大的子数组的和。这个解法的时间复杂度是 O(n),内存消耗O(n)。代码如下maxSubArray_dp_normal函数所示。由于在计算dp[i]的时候只需要用到dp[i-1],所有可以用一个变量代替dp数组,进行了空间压缩,实现时间复杂度是 O(n),内存消耗O(1),是本题的最优解法,代码如maxSubArray_dp_selected函数所示。
动态规划:
//动态规划 时间O(n) 空间O(n)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
//return maxSubArray_dp_normal(nums);
return maxSubArray_dp_seleted(nums);
}
int maxSubArray_dp_normal(vector<int>& nums)
{
const int nums_size = nums.size();
vector<int> dp = nums;//dp[i]表示以nums[i]结尾的和最大的子序列的和
int res = dp[0];
for(int i = 1;i<nums_size;++i)
{
dp[i] = max(dp[i-1]+nums[i],nums[i]);//状态转移方程
res = max(res,dp[i]);
}
return res;
}
//空间优化了的dp,时间O(n) 空间O(1)
int maxSubArray_dp_seleted(vector<int> &nums)
{
const int nums_size = nums.size();
int dp = nums[0];
int res = dp;
for(int i = 1;i<nums_size;++i)
{
dp = max(dp+nums[i],nums[i]);//状态转移方程
res = max(res,dp);
}
return res;
}
};
题目还要求我们用分治法 Divide and Conquer Approach 来解,这个分治法的思想就类似于二分搜索法,需要把数组一分为二,分别找出左边和右边的最大子数组之和,然后还要考虑最大和子数组同时跨越左右两个子数组的情况,具体是从中间开始向左右分别扫描,求出的最大值分别和左右两边得出的最大值相比较取三者最大的那一个,时间复杂度是O(nlogn),显然此种方法不是本题的最优解,分治法代码如下:
分治:
1
class Solution {
public:
int maxSubArray(std::vector<int>& nums) {
int nums_size = nums.size();
return maxSubArray_div(nums,0,nums_size-1);
}
int maxSubArray_div(std::vector<int>& nums,int i,int j)
{
if(i>=j) return nums[i];
int h = (j+i)/2;//也可以使用移位操作
int left = maxSubArray_div(nums,i,h);
int right = maxSubArray_div(nums,h+1,j);
int mid_value_right = nums[h];
int r_sum = 0;
for(int r = h;r<=j;++r)
{
r_sum+=nums[r];
if(r_sum>=mid_value_right) mid_value_right=r_sum;
}
int mid_value_left = nums[h];
int l_sum = 0;
for(int l = h;l>=i;--l)
{
l_sum+=nums[l];
if(l_sum>=mid_value_left) mid_value_left=l_sum;
}
int mid_value = mid_value_left + mid_value_right - nums[h];
return max(max(left,right),mid_value);
}
};