【动态规划】LeetCode 53. Maximum Subarray

LeetCode 53. Maximum Subarray

      原题描述(求子序列最大和/最大子串):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.

      在本篇文章中贴几个除了暴力求解外的方法,整理自博客http://www.cnblogs.com/waytofall/archive/2012/04/10/2439820.html和博客http://www.cnblogs.com/boring09/p/4252780.html。

      方法一:动态规划

      贴一个知乎上关于动态规划的讨论链接https://www.zhihu.com/question/23995189

      令sum[i]是以第i个元素结尾且和最大的子串。对于数组元素a[i],以a[i]结尾且和最大的子串,要么是以第i-1个元素结尾且和最大(sum[i-1])的子串加a[i],要么是只包含a[i]元素,即sum[i] = max(sum[i-1] + a[i], a[i]),亦即等价于判断sum[i-1]是否大于0。

可以得到此题的代码如下:

class Solution {
public:
    int maxSubArray(vector& nums) {
    int sum = nums[0];
    int maxSum = sum;
    for (int i=1;i

时间复杂度O(n),空间复杂度O(1)。

      方法二:扫描法

      个人感觉和动态规划一样只是理解方式不同而已。。后加注:这里提到的扫描法存在一个问题就是如果最大字段和小于0则算法没法给出正确答案。其实这个问题用动态规划就好,这里的扫描法其实真的不是个好方法,只是因为很有名所以还是粘出来了。
      当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。实现:
//copyright@ July 2010/10/18  
//updated,2011.05.25.  
#include   
  
int maxSum(int* a, int n)  
{  
    int sum=0;  
    //其实要处理全是负数的情况,很简单,如稍后下面第3点所见,直接把这句改成:"int sum=a[0]"即可  
    //也可以不改,当全是负数的情况,直接返回0,也不见得不行。  
    int b=0;  
      
    for(int i=0; isum,则更新sum=b; 
若b

      时间复杂度O(n),空间复杂度O(1)

      方法三:分而治之

假设求A[l..r]的最大子串和

首先将其分成两半A[l..m]和A[m+1..r],其中m=(l+r)/2,并分别求递归求出这两半的最大子串和,不妨称为left,right。如下图所示:

【动态规划】LeetCode 53. Maximum Subarray_第1张图片

A[l..r]的连续子串和可能出现在左半边(即left),或者可能出现在右半边(即right),还可能出现在横跨左右两半的地方(即middle),如下图橙色部分所示:

【动态规划】LeetCode 53. Maximum Subarray_第2张图片

当然,middle完全有可能覆盖left或right,它可能的范围入下图所示:

【动态规划】LeetCode 53. Maximum Subarray_第3张图片

那么,如何求middle?貌似没有什么简单的方法,只能从中间向两遍扫,也就是把上图种的范围扫一遍。具体怎么扫呢?见方法I和方法II

是不是突然觉得很坑爹?既然知道最后求middle要扫一遍,还不如一开始就从l到r扫一遍求max得了,还费什么劲儿求left和right呢?求left和right的作用仅限于缩小扫描的范围。

int diveNConquer(int A[], int l, int r) {
  if (l == r)
    return A[l];

  int m = (l + r) / 2;
  int left = diveNConquer(A, l, m);
  int right = diveNConquer(A, m + 1, r);
  int middle = A[m];
  for (int i = m - 1, tmp = middle; i >= l; i--) {
    tmp += A[i];
    middle = max(middle, tmp);
  }
  for (int i = m + 1, tmp = middle; i <= r; i++) {
    tmp += A[i];
    middle = max(middle, tmp);
  }

  return max(middle, max(left, right));
}

int maxSubArray(int A[], int n) {
  return diveNConquer(A, 0, n - 1);
}
      分析一下时间复杂度,设问题的工作量是T(n),则有T(n) = 2T(n/2) + O(n),解得T(n) = O(nlogn)。看看,效率反而低了不少。














你可能感兴趣的:(LeetCode练习题)