8. 最大子数组和【53】

146 阅读2分钟

题目:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

关键思路:

子数组,是连续部分,故排除排序优化。

仔细思考一下,这是一个典型的DP,分析如下:

因为可以产生递推关系, 采用动态规划时, 经常通过遍历方式, 如 背包问题, 最大公共子串 , 这里的动态规划解法也是以 先遍历出 以某个节点为结束节点的所有子序列 的思路。

对于刚接触动态规划的, 我感觉熟悉第三种遍历方式是需要抓住的核心。

因为我们通常的惯性思维是以子序列的开头为基准,先遍历出以 a 为开头的所有子序列,再遍历出以 b 为开头的...但是动态规划为了找到不同子序列之间的递推关系,恰恰是以子序列的结束点为基准的,也就是a[i],这点开阔了我们的思路。

状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}   
// 有一个数组不断的计算前序状状态。
// 简言之,就是数学里,Sn与Sn-1之间的推导关系。

在每一个扫描点计算以该点数值为结束点的子数列的最大和(正数和)

1.求出每一项与之前的和(arr)

2.负数不必再相加(负数为一个子数组的分界线)

3.正数继续相加(表示多个子数组)

4.求数组里的最大值(最后求出正数的最大值,最大子数组集合)

解:

方法1:
var maxSubArray = function (nums) {
  if (!nums) {
    return 0
  }
  if (nums.length === 1) {
    return nums[0]
  }
  let Sn = [nums[0]];
  for (let i = 1; i < nums.length; i++) {
    if(Sn[i-1] + nums[i] < nums[i]) {
      Sn[i] = nums[i];
    } else {
      Sn[i] = Sn[i-1] + nums[i]
    }
  }
  return Math.max(...Sn)
}

将所有的历史和存储在一个list中,即维护了一个空间来存储所有的Sn,又额外的堆空间使用。

怎样调整呢?优化掉这个堆空间的使用。

方法2:

var maxSubArray = function (nums) {
  if (!nums) {
    return 0
  }
  if (nums.length === 1) {
    return nums[0]
  }
  let Sn = nums[0];
  let max = nums[0];
  for (let i = 1; i < nums.length; i++) {
    if (Sn + nums[i] < nums[i]) {
      Sn = nums[i];
    } else {
      Sn = Sn + nums[i]
    }
    max = max > Sn ? max : Sn;
  }
  return max;
}

将方法一一个堆变量变成两个普通栈变量。

———— 前端、Javascript实现、算法、刷题、leetcode