题目描述:
一个整数数组中的元素有正有负,在该数组中找出一个连续子数组,要求该连续子数组中各元素的和最大,这个连续子数组便被称作最大连续子数组。比如数组{2,4,-7,5,2,-1,2,-4,3}的最大连续子数组为{5,2,-1,2},最大连续子数组的和为5+2-1+2=8。
这是一道非常经典的动态规划的题目,用到的思路我们在别的动态规划题目中也很常用,以后我们称为”局部最优和全局最优解法“。
基本思路是这样的,在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解是,一个是局部最优,就是必须包含当前元素的最优的解。接下来说说动态规划的递推式(这是动态规划最重要的步骤,递归式出来了,基本上代码框架也就出来了)。假设我们已知第i步的global[i](全局最优)和local[i](局部最优),那么第i+1步的表达式是:
local[i+1]=Math.max(A[i], local[i]+A[i]),就是局部最优是一定要包含当前元素,所以不然就是上一步的局部最优local[i]+当前元素A[i](因为local[i]一定包含第i个元素,所以不违反条件),但是如果local[i]是负的,那么加上他就不如不需要的,所以不然就是直接用A[i];
global[i+1]=Math(local[i+1],global[i]),有了当前一步的局部最优,那么全局最优就是当前的局部最优或者还是原来的全局最优(所有情况都会被涵盖进来,因为最优的解如果不包含当前元素,那么前面会被维护在全局最优里面,如果包含当前元素,那么就是这个局部最优)。
接下来我们分析一下复杂度,时间上只需要扫描一次数组,所以时间复杂度是O(n)。空间上我们可以看出表达式中只需要用到上一步local[i]和global[i]就可以得到下一步的结果,所以我们在实现中可以用一个变量来迭代这个结果,不需要是一个数组,也就是如程序中实现的那样,所以空间复杂度是两个变量(local和global),即O(2)=O(1)。
代码如下:
- public int maxSubArray(int[] A) {
- if(A==null || A.length==0)
- return 0;
- int global = A[0];
- int local = A[0];
- for(int i=1;i<A.length;i++)
- {
- local = Math.max(A[i],local+A[i]);
- global = Math.max(local,global);
- }
- return global;
- }
教程参考:屈婉玲的youtube视频
function FindGreatestSumOfSubArray(array)
{
// write code here
var tempMax = array[0],max = array[0];
for(var i=1;i<array.length;i++){
max = Math.max(max+array[i],array[i]);
if(max>tempMax){
tempMax = max;
}
}
return tempMax;
}
很少用Math.max的形式,更多用if,因为Math.max只能用于单条语句。
第一次写的时候,使用
function FindGreatestSumOfSubArray(array)
{
// write code here
var tempMax = 0,max = 0;
for(var i=0;i<array.length;i++){
max = Math.max(max+array[i],array[i]);
if(max>tempMax){
tempMax = max;
}
}
return tempMax;
}
因为数组元素不一定全是正数,所以这里设置tempMax = 0,max = 0;作为最小值是不对的。应该设置tempMax = array[0],max = array[0];
相同类型的题目:
Jump Game -- LeetCode
原题链接: http://oj.leetcode.com/problems/jump-game/
这道题是动态规划的题目,所用到的方法跟是在Maximum Subarray中介绍的套路,用“局部最优和全局最优解法”。我们维护一个到目前为止能跳到的最远距离,以及从当前一步出发能跳到的最远距离。局部最优local=A[i]+i,而全局最优则是global=Math.max(global, local)。递推式出来了,代码就比较容易实现了。因为只需要一次遍历时间复杂度是O(n),而空间上是O(1)。代码如下:
- public boolean canJump(int[] A) {
- if(A==null || A.length==0)
- return false;
- int reach = 0;
- for(int i=0;i<=reach&&i<A.length;i++)
- {
- reach = Math.max(A[i]+i,reach);
- }
- if(reach<A.length-1)
- return false;
- return true;
- }
这也是一道比较经典的动态规划的题目,不过不同的切入点可能会得到不同复杂度的算法,比如如果维护的历史信息是某一步是否能够到达,那么每一次需要维护当前变量的时候就需要遍历前面的所有元素,那么总的时间复杂度就会是O(n^2)。所以同样是动态规划,有时候也会有不同的角度,不同效率的解法。这道题目还有一个扩展Jump Game II,有兴趣的朋友可以看看。