题目
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.
算法
分治算法
复杂度:O(nlgn)
由于我们知道最大子序列可能存在于A数组的左边,右边,或者一点左边一点右边。
所以我们很容易可以联想到,居然这样我们可以把A数组划分成若干个小的子数组,对子数组求出左边的最大值,和右边的最大值,再求出从中间位置到左边的某个位置的最大值、从中间位置到右边的某个位置的最大值,得到了这四个值之后剩下的我们就可以通过比较得到这个子数组的最大值了。(递归的过程)
int myMaxSubArray(int A[], int m, int n)
{
if (m == n) return A[m];
int mid = (m+n)/2;
int maxPre = myMaxSubArray(A, m, mid);
int maxEnd = myMaxSubArray(A, mid+1, n);
int leftMax = INT_MIN;
int rightMax = INT_MIN;
int sum = 0;
for (int i = mid; i >= m; i--)
{
sum += A[i];a
if (sum > leftMax)
leftMax = sum;
}
sum = 0;
for (int i = mid+1; i <=n; i++)
{
sum += A[i];
if (sum > rightMax)
rightMax = sum;
}
sum = leftMax + rightMax;
return std::max(sum, std::max(maxPre, maxEnd));
}
int maxSubArray(int A[], int n)
{
if (n <= 0) return 0;
return myMaxSubArray(A, 0, n-1);
}
算法2
DP
O(N)
// 状态转移方程
sum[i]=max{sum[i-1]+a[i],a[i]}
//贪心可能无法达到最优解
在动态规划算法中,每步所作的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能作出选择。而在贪心算法中,仅在当前状态下作出最好选择,即局部最优选择。然后再去解作出这个选择后产生的相应的子问题。贪心算法所作的贪心选择可以依赖于以往所作过的选择,但决不依赖于将来所作的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。
当我们从头到尾遍历这个数组的时候,对于数组里的一个整数,它有几种选择呢?它只有两种选择: 1、加入之前的SubArray;2. 自己另起一个SubArray。那什么时候会出现这两种情况呢?
如果之前SubArray的总体和大于0的话,我们认为其对后续结果是有贡献的。这种情况下我们选择加入之前的SubArray
如果之前SubArray的总体和为0或者小于0的话,我们认为其对后续结果是没有贡献,甚至是有害的(小于0时)。这种情况下我们选择以这个数字开始,另起一个SubArray。
class Solution {
public:
int maxSubArray(vector<int>& A) {
int n=A.size();
if(n==0)
return 0;
int sum=A[0],mmax=A[0];
for(int i=1;i<n;i++){
if(sum<0)
sum=A[i];
else
sum+=A[i];
mmax=max(mmax,sum);
}
return mmax;
}
};
DP:局部最优和全局最优解法
- 维护两个变量,一个是局部最优(必须包括当前元素的解),一个是全局最优(当前元素为止的最优解)
- 假设已知:第
i
步的global[i](全局最优)和local[i](局部最优),那么第
i+1 步的表达式
local [i+1]=max(A[i],local[i]+A[i]) ( 局部最优必须有当前元素)
global[i+1]=max(local[i+1],globa[i])(全局最优=当前的局部最优或者是原来的全局最优)
复杂度:O(N)
这里写代码片