最大子数组积-2

原题描述参考: leetcode’s 152 Maximum Product Subarray

解法1请参考 我前面的文章

动态规划的解法

动态规划,是递归(或遍历) + 缓存的技术; 此题一遍遍历就已经可以得到累积的结果了比较直观; 解释在第一个js版本的注释中

/**
具体例子:
idx:    0,   1,   2,   3,  4 .... n-1
nums:  2,    3,  -1,   -3, 2 ....

dpMax: 2,  6, -1=max(-6, -1), 18=max(-3*-1, -3*-6), 36=max(,,)
dpMin: 2,  2, -6, -6=min(-3*-1, -3*-6, -6), 

初始化:
dpMax[0] = nums[0];
dpMin[0] = nums[0];

由于序列中存在负数也存在整数,所以在遍历时, 需要保存最大正数和最大负数的求积数组;

而实际上dpMax, dpMin的当前元素的值是从以下3个元素的值中取max或min得到的; 这是从若干种情况下合并状态得到的(e.g. 当取nums[i]时, 例如具体例子的idx=2时取dpMax取-1); 注意子序列起始点不确定是哪个idx, 所以需要一个max或ans来记录最后的取值;

dpMax[i-1] = max(nums[i] * dpMax[i-1], nums[i] * dpMin[i-1], nums[i]);
dpMin[i-1] = min(nums[i] * dpMax[i-1], nums[i] * dpMin[i-1], nums[i]);

时间复杂度: O(n)
空间复杂度: O(n)
*/
function maxArrayProduct(nums) {
  const dpMax = [];
  const dpMin = [];

  let max = nums[0];
  dpMax[0] = dpMin[0] = nums[0];
  const n = nums.length;
  for (let i=1; i<n; ++i) {
    dpMax[i] = Math.max(nums[i] * dpMax[i-1], nums[i] * dpMin[i-1], nums[i]);
    dpMin[i] = Math.min(nums[i] * dpMax[i-1], nums[i] * dpMin[i-1], nums[i]);
    max = Math.max(max, dpMax[i]);
  }

  return max;
}

可以看到dpMax[i]的值只会用到前一个元素的值, 优化方案这个和滚动数组的优化有些类似, 我们不需要用数组(dpMax, dpMin), 每次只需要保存上一次的值和更新这一次的值, 因此可以得到对空间复杂度优化的版本:

function maxArrayProduct(nums) {
  let dpMax, dpMin;
  let max = nums[0];
  dpMax = dpMin = nums[0];

  const n = nums.length;
  for (let i=1; i<n; ++i) {
    let preMax = dpMax;
    dpMax = Math.max(nums[i] * dpMax, nums[i] * dpMin, nums[i]);
    dpMin = Math.min(nums[i] * preMax, nums[i] * dpMin, nums[i]);
    max = Math.max(max, dpMax);
  }

  return max;
}

C++的版本

#include <iostream>
#include <algorithm>

int maxArrayProduct(const vector<int> &nums) {
  int n = nums.size();
  int dpMax, dpMin;
  int max = nums[0];
  dpMax = dpMin = nums[0];

  for (int i = 0; i < n; ++i) {
    int preMax = dpMax;
    dpMax = std::max(nums[i] * dpMin, std::max(nums[i] * dpMax, nums[i]));
    dpMin = std::min(nums[i] * preMax, std::max(nums[i] * dpMin, nums[i]));
    max = std::max(max, dpMax);
  }
  return max;
}

可以看到动态规划的思路的版本,确实比按0分区且计数负数奇偶数的版本代码简洁很多。时空复杂度也基本一致;
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章