最大乘积子数组问题解题笔记 问题描述: 给定一个整数数组 nums,要求找到一个连续的子数组(子数组中至少有一个数字),使得该子数组中的元素乘积最大。返回这个子数组的乘积。 例如: 输入: nums = [2, 3, -2, 4] 输出: 6 解释: 子数组 [2, 3] 的乘积最大,为 6。 输入: nums = [-2, 0, -1] 输出: 0 解释: 乘积为 0,而 0 是可能的最大值。 问题分析: 在这个问题中,我们需要从数组中选取一个连续子数组,其元素的乘积最大。由于乘积的特点是会受到正负号变化的影响,尤其是当遇到负数时,正负交替的情况会导致最大乘积的变化。 关键思路:
1.正负号影响: 当遇到负数时,负数可能使得前面乘积为负的子数组变为正数。也可能与其他负数相乘,变成一个更大的正数。因此,除了记录当前的最大乘积外,我们还需要记录当前的最小乘积。 2.动态规划思想: 这类问题常常可以通过动态规划来解决。我们定义两个状态:
3.max_prod[i]: 以 nums[i] 为结尾的最大乘积。 4.min_prod[i]: 以 nums[i] 为结尾的最小乘积。 因为我们关心的是乘积的最大值,而最大值可能由负数乘积变为正数,或者由正数乘积变为更大的正数。
5.状态转移:
6.当前的 max_prod[i] 可以由以下几种情况决定:
7.直接继承前一个最大乘积,即 max_prod[i-1] * nums[i]。 8.继承前一个最小乘积(如果是负数的话,可能使得当前乘积变得更大),即 min_prod[i-1] * nums[i]。 9.直接从 nums[i] 开始,即 nums[i] 本身。 10.类似地,min_prod[i] 也有三种可能的选择:
11.min_prod[i-1] * nums[i]。 12.max_prod[i-1] * nums[i]。 13.nums[i] 本身。
14.最终结果: 我们需要在每一步都更新当前的最大乘积 max_prod[i],并且跟踪所有的最大值,最后返回最大乘积。
解题步骤:
15.初始化变量 max_prod 和 min_prod 为 nums[0],表示从第一个元素开始。 16.遍历数组,对于每个元素,更新 max_prod 和 min_prod,同时维护一个 global_max,记录当前最大的乘积。 17.遍历结束后,返回 global_max 即为答案。
代码实现: def maxProduct(nums): if not nums: return 0
max_prod = min_prod = global_max = nums[0] # 初始化为第一个元素
for num in nums[1:]:
# 如果当前数字为负数,交换最大值和最小值
if num < 0:
max_prod, min_prod = min_prod, max_prod
# 更新最大乘积和最小乘积
max_prod = max(num, max_prod * num)
min_prod = min(num, min_prod * num)
# 更新全局最大值
global_max = max(global_max, max_prod)
return global_max
解释:
18.初始化 max_prod, min_prod, 和 global_max 为数组的第一个元素。 19.遍历数组中的每个数字:
20.如果当前数字为负数,交换 max_prod 和 min_prod,因为负数会导致最大值变成最小值,最小值变成最大值。 21.然后更新 max_prod 和 min_prod,分别为当前数字和前面的最大或最小乘积。 22.更新全局最大值 global_max。
23.最后返回 global_max。
时间复杂度:
24.时间复杂度为 O(n),其中 n 是数组 nums 的长度。我们只需要遍历数组一次,每次更新常数时间内完成。
空间复杂度:
25.空间复杂度为 O(1),只需要常数空间来存储变量。
边界情况:
26.数组为空: 如果输入数组为空,应该返回 0(没有子数组)。 27.数组中有零: 零不会改变最大乘积的符号,但会导致乘积归零,因此需要特别处理。 28.数组中有负数: 负数对最大乘积有较大影响,动态维护最大和最小乘积值尤为重要。
总结: 最大乘积子数组问题是一个典型的动态规划问题。通过维护当前子数组的最大和最小乘积,可以有效地解决这个问题。这个方法不仅能应对正负交替的情况,还能处理零和负数的特殊情况,确保得到正确的最大乘积。