最大子數組積-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分區且計數負數奇偶數的版本代碼簡潔很多。時空複雜度也基本一致;
在這裏插入圖片描述

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