Maximum Subarray连续子序列最大和-动态规划之和的局部最优和全局最优

 

题目描述:

  一个整数数组中的元素有正有负,在该数组中找出一个连续子数组,要求该连续子数组中各元素的和最大,这个连续子数组便被称作最大连续子数组。比如数组{2,4,-7,5,2,-1,2,-4,3}的最大连续子数组为{5,2,-1,2},最大连续子数组的和为5+2-1+2=8。

 

原题链接:  http://oj.leetcode.com/problems/maximum-subarray/  
这是一道非常经典的动态规划的题目,用到的思路我们在别的动态规划题目中也很常用,以后我们称为”局部最优和全局最优解法“。
基本思路是这样的,在每一步,我们维护两个变量,一个是全局最优,就是到当前元素为止最优的解是,一个是局部最优,就是必须包含当前元素的最优的解。接下来说说动态规划的递推式(这是动态规划最重要的步骤,递归式出来了,基本上代码框架也就出来了)。假设我们已知第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)。
代码如下: 
[java]  view plain  copy
 
  1. public int maxSubArray(int[] A) {  
  2.     if(A==null || A.length==0)  
  3.         return 0;  
  4.     int global = A[0];  
  5.     int local = A[0];  
  6.     for(int i=1;i<A.length;i++)  
  7.     {  
  8.         local = Math.max(A[i],local+A[i]);  
  9.         global = Math.max(local,global);  
  10.     }  
  11.     return global;  
  12. }  
这道题虽然比较简单,但是用到的动态规划方法非常的典型,我们在以后的题目中还会遇到,大家还是要深入理解一下哈。我现在记得的用到的题目是 Jump Game ,以后有统计一下再继续更新。

 

 

 

教程参考:屈婉玲的youtube视频

 
以下为原创:
 
我的风格写法(使用javascript):
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)。代码如下: 

[java]  view plain  copy
  1. public boolean canJump(int[] A) {  
  2.     if(A==null || A.length==0)  
  3.         return false;  
  4.     int reach = 0;  
  5.     for(int i=0;i<=reach&&i<A.length;i++)  
  6.     {  
  7.         reach = Math.max(A[i]+i,reach);  
  8.     }  
  9.     if(reach<A.length-1)  
  10.         return false;  
  11.     return true;  
  12. }  

这也是一道比较经典的动态规划的题目,不过不同的切入点可能会得到不同复杂度的算法,比如如果维护的历史信息是某一步是否能够到达,那么每一次需要维护当前变量的时候就需要遍历前面的所有元素,那么总的时间复杂度就会是O(n^2)。所以同样是动态规划,有时候也会有不同的角度,不同效率的解法。这道题目还有一个扩展Jump Game II,有兴趣的朋友可以看看。

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最大连续子序列和问题是指在一个序列中,找到一个连续子序列,使得它们的和最大。这个问题可以使用分治法和动态规划算法来解决。 1. 分治法 使用分治法求解最大连续子序列和问题的思路是:将原序列分成两个部分,最大子序列可能存在于左半部分、右半部分或跨越左右两个部分。分别计算这三种情况下的最大子序列和,然后取最大值即可。 下面是使用分治法求解最大连续子序列和问题的Python代码示例: ```python def max_subarray(nums: List[int]) -> int: return _max_subarray(nums, 0, len(nums) - 1) def _max_subarray(nums: List[int], left: int, right: int) -> int: if left == right: return nums[left] mid = (left + right) // 2 left_max = _max_subarray(nums, left, mid) right_max = _max_subarray(nums, mid + 1, right) # 计算跨越左右两个部分的最大子序列和 cross_max = nums[mid] left_cross_max = nums[mid] for i in range(mid - 1, left - 1, -1): left_cross_max += nums[i] cross_max = max(cross_max, left_cross_max) right_cross_max = nums[mid + 1] for i in range(mid + 2, right + 1): right_cross_max += nums[i] cross_max = max(cross_max, right_cross_max) return max(left_max, right_max, cross_max) ``` 在上面的代码中,我们使用递归的方式将原序列分成两个部分,然后计算跨越左右两个部分的最大子序列和、左半部分的最大子序列和、右半部分的最大子序列和,取三者中的最大值作为整个序列的最大子序列和。 2. 动态规划 使用动态规划算法求解最大连续子序列和问题的思路是:从头开始遍历序列,对于每一个位置i,计算以i结尾的最大子序列和,然后取所有的最大子序列和的最大值作为问题的解。 具体来说,我们定义一个数组dp,其中dp[i]表示以i结尾的最大子序列和。对于dp[i]来说,它的值可以由dp[i-1]和nums[i]计算得到,即dp[i] = max(dp[i-1] + nums[i], nums[i])。 下面是使用动态规划算法求解最大连续子序列和问题的Python代码示例: ```python def max_subarray(nums: List[int]) -> int: dp = [0] * len(nums) dp[0] = nums[0] max_sum = nums[0] for i in range(1, len(nums)): dp[i] = max(dp[i-1] + nums[i], nums[i]) max_sum = max(max_sum, dp[i]) return max_sum ``` 在上面的代码中,我们使用一个循环遍历整个序列,计算以每一个位置为结尾的最大子序列和,并且更新全局最大值。 希望这个回答能够帮到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值