LeetCode 热题 100_乘积最大子数组(88_152_中等_C++)(动态规划)
LeetCode 热题 100_乘积最大子数组(88_152)
- 题目描述:
- 输入输出样例:
- 题解:
- 解题思路:
- 思路一(暴力破解法(双重循环)):
- 思路二(动态规划):
- 代码实现
- 代码实现(思路一(暴力破解法)):
- 代码实现(思路二(哈希表)):
- 以思路二为例进行调试
题目描述:
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
输入输出样例:
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
提示:
1 <= nums.length <= 2 * 104
-10 <= nums[i] <= 10
nums 的任何子数组的乘积都 保证 是一个 32-位 整数
题解:
解题思路:
思路一(暴力破解法(双重循环)):
1、计算所有的子数组的乘积,记录其中乘积的最大值,每个子数组的范围为 left~right(left 和 right 代表数组下标)。
2、复杂度分析:
① 时间复杂度:O(n2),n代表数组中元素的个数,使用了双重循环。
② 空间复杂度:O(1)
思路二(动态规划):
1、因数组中的元素既有正数又有负数,这样我们需要分情况讨论(由于负数的存在,当前最小乘积在遇到负数时可能会转为最大乘积,从而有可能影响最终的最大值。)。此解法通过动态维护当前最大乘积和最小乘积来有效地解决问题。
四种情况如下:
假设有nums=[a,b],假设a!=0,b!=0。
- a>0,b>0 乘积最大为 a*b。
- a>0,b<0 乘积最大为 a。
- a<0,b>0 乘积最大为 b。
- a<0,b<0 乘积最大为 a*b。
例:nums = [2,3,-2,4]
之前的最大乘积为:pre_max,之前的最小乘积为:pre_min
当前的最大乘积为:cur_max,当前的最小乘积为:cur_min
① i=0 ,初始化 pre_max = pre_min = cur_max = cur_min = nums[ 0 ] = 2 。
② i=1 ,cur_max = max{nums[ 1 ],nums[ 1 ] × pre_min,nums[ 1 ] × pre_max } =6, cur_min = min{nums[ 1 ],nums[ 1 ] × pre_min,nums[ 1 ] × pre_max} = 3。
③ i=2,cur_max = max{nums[ 2 ],nums[ 2 ] × pre_min,nums[ 2 ] × pre_max } = -2,cur_min = min{nums[ 2 ],nums[ 2 ] × pre_min,nums[ 2 ] × pre_max} = -12。
④ i=3,cur_max = max{nums[ 3 ],nums[ 3 ] × pre_min,nums[ 3 ] × pre_max } = 4,cur_min = min{nums[ 3 ],nums[ 3 ] × pre_min,nums[ 3 ] × pre_max} = -48。
在此过程中记录最大的 cur_max=6 最为乘积最大子数组。
3、复杂度分析
① 时间复杂度:O(n),n代表数组中元素的个数。因只遍历一遍数组。
② 空间复杂度:O(1)。
代码实现
代码实现(思路一(暴力破解法)):
class Solution1 {
public:// 函数:maxProduct,计算给定数组中子数组的最大乘积int maxProduct(vector<int> &nums) {// 初始化答案为最小的整数值,后续将通过比较更新答案int ans = INT_MIN;// 外层循环:从每个可能的子数组起始位置开始for (int left = 0; left < nums.size(); left++) {// 初始化当前子数组的乘积为 1int cur_product = 1;// 内层循环:从 left 位置开始向右遍历,计算当前子数组的乘积for (int right = left; right < nums.size(); right++) {// 更新当前子数组的乘积cur_product *= nums[right];// 更新答案为当前子数组的乘积和之前答案的较大值ans = max(cur_product, ans);}}// 返回找到的最大乘积return ans;}
};
代码实现(思路二(哈希表)):
class Solution2 {
public:// 函数:maxProduct,计算给定数组中子数组的最大乘积int maxProduct(vector<int> &nums) {// 初始化答案为数组的第一个元素long long ans = nums[0];// 初始化当前最大乘积和最小乘积,均为数组的第一个元素long long cur_max = nums[0], cur_min = nums[0];// 从第二个元素开始遍历数组for (int i = 1; i < nums.size(); i++) {// 保存前一次的最大乘积和最小乘积long long pre_max = cur_max, pre_min = cur_min;// 计算当前最大乘积:// 1. nums[i] 本身// 2. nums[i] * 前一次的最大乘积// 3. nums[i] * 前一次的最小乘积cur_max = max(max((long long)nums[i], nums[i] * pre_max), (long long)nums[i] * pre_min);// 计算当前最小乘积:// 1. nums[i] 本身// 2. nums[i] * 前一次的最大乘积// 3. nums[i] * 前一次的最小乘积cur_min = min(min((long long)nums[i], nums[i] * pre_max), (long long)nums[i] * pre_min);// 更新答案为当前最大乘积和之前答案的较大值ans = max(ans, cur_max);}// 返回找到的最大乘积return ans;}
};
以思路二为例进行调试
#include<iostream>
#include<vector>
using namespace std;class Solution2 {
public:// 函数:maxProduct,计算给定数组中子数组的最大乘积int maxProduct(vector<int> &nums) {// 初始化答案为数组的第一个元素long long ans = nums[0];// 初始化当前最大乘积和最小乘积,均为数组的第一个元素long long cur_max = nums[0], cur_min = nums[0];// 从第二个元素开始遍历数组for (int i = 1; i < nums.size(); i++) {// 保存前一次的最大乘积和最小乘积long long pre_max = cur_max, pre_min = cur_min;// 计算当前最大乘积:// 1. nums[i] 本身// 2. nums[i] * 前一次的最大乘积// 3. nums[i] * 前一次的最小乘积cur_max = max(max((long long)nums[i], nums[i] * pre_max), (long long)nums[i] * pre_min);// 计算当前最小乘积:// 1. nums[i] 本身// 2. nums[i] * 前一次的最大乘积// 3. nums[i] * 前一次的最小乘积cur_min = min(min((long long)nums[i], nums[i] * pre_max), (long long)nums[i] * pre_min);// 更新答案为当前最大乘积和之前答案的较大值ans = max(ans, cur_max);}// 返回找到的最大乘积return ans;}
};int main(int argc, char const *argv[])
{vector<int> nums={1,0,-5,2,3,-8,-9};Solution2 s;cout<<s.maxProduct(nums);return 0;
}
LeetCode 热题 100_乘积最大子数组(88_152)原题链接
欢迎大家和我沟通交流(✿◠‿◠)