1. 题目
读题
https://leetcode.cn/problems/maximum-product-subarray/
考查点
2. 解法
思路
思路是这样的:我们要找到一个子数组,使得它的乘积最大。我们可以用动态规划的方法,定义一个状态dp[i],表示以第i个元素结尾的子数组的最大乘积。那么我们要求的就是dp[0]到dp[n-1]中的最大值,其中n是数组的长度。但是这样定义状态有一个问题,就是如果第i个元素是负数,那么它可能会把之前的最大乘积变成最小乘积,或者把之前的最小乘积变成最大乘积。所以我们需要同时维护两个状态,一个是max[i],表示以第i个元素结尾的子数组的最大乘积,另一个是min[i],表示以第i个元素结尾的子数组的最小乘积。那么我们要求的就是max[0]到max[n-1]中的最大值。那么如何更新这两个状态呢?我们可以根据第i个元素的正负号来分情况讨论:
- 如果第i个元素是正数,那么它会使得之前的最大乘积变得更大,也会使得之前的最小乘积变得更小。所以我们有:
- max[i] = max(nums[i], max[i-1] * nums[i])
- min[i] = min(nums[i], min[i-1] * nums[i])
- 如果第i个元素是负数,那么它会使得之前的最大乘积变成最小乘积,也会使得之前的最小乘积变成最大乘积。所以我们有:
- max[i] = max(nums[i], min[i-1] * nums[i])
- min[i] = min(nums[i], max[i-1] * nums[i])
- 如果第i个元素是0,那么它会使得之前的任何乘积都变成0。所以我们有:
- max[i] = 0
- min[i] = 0
由于每个状态只和前一个状态有关,所以我们可以用两个变量来代替数组,节省空间。另外,由于负数会导致最大和最小乘积交换,所以我们在更新之前要先判断当前元素的正负号,并且如果是负数就交换两个变量的值。
代码逻辑
- 步骤一:判断数组是否为空或者长度为0,如果是,就返回0,因为没有子数组。
- 步骤二:初始化两个变量max和min,分别表示以第一个元素结尾的子数组的最大乘积和最小乘积,也就是第一个元素本身。同时,初始化一个全局变量result,表示最终要返回的最大乘积,也就是max的初始值。
- 步骤三:从第二个元素开始遍历数组,对于每个元素,执行以下的子步骤:
- 子步骤一:判断当前元素的正负号,如果是负数,就交换max和min的值,因为负数会导致最大和最小乘积反转。
- 子步骤二:更新max和min的值,根据之前的思路,用当前元素和当前元素乘以之前的max或min来比较,取较大或较小的值作为新的max或min。
- 子步骤三:更新result的值,用当前的max和之前的result来比较,取较大的值作为新的result。
- 步骤四:遍历结束后,返回result作为答案。
具体实现
class Solution { public int maxProduct(int[] nums) { // edge case if (nums == null || nums.length == 0) return 0; // initialize two variables to store the max and min product ending with current element int max = nums[0]; int min = nums[0]; // initialize a global variable to store the max product int result = max; // loop through the array for (int i = 1; i < nums.length; i++) { // if current element is negative, swap max and min if (nums[i] < 0) { int temp = max; max = min; min = temp; } // update max and min max = Math.max(nums[i], max * nums[i]); min = Math.min(nums[i], min * nums[i]); // update result result = Math.max(result, max); } return result; } }
3. 总结