leetcode
leetcode 1651 ~ 1700
子数组最小乘积的最大值

子数组最小乘积的最大值

难度:

标签:

题目描述

代码结果

运行时间: 235 ms, 内存: 32.3 MB


/*
 题目思路:
 1. 使用Java Stream处理时,重点在于如何高效计算子数组的和及最小值。
 2. 可以考虑先计算每个元素作为最小值时的左、右边界,然后通过stream计算总和。
 3. 由于stream主要用在简化代码结构上,这里示例代码尽量精简。
 */
import java.util.*;

public class Solution {
    public int maxSumMinProduct(int[] nums) {
        int MOD = 1_000_000_007;
        int n = nums.length;
        long[] prefixSum = new long[n + 1];
        for (int i = 0; i < n; i++) {
            prefixSum[i + 1] = prefixSum[i] + nums[i];
        }
        Stack<Integer> stack = new Stack<>();
        long maxProduct = 0;
        for (int i = 0; i <= n; i++) {
            while (!stack.isEmpty() && (i == n || nums[stack.peek()] > nums[i])) {
                int minIndex = stack.pop();
                int left = stack.isEmpty() ? -1 : stack.peek();
                long sum = prefixSum[i] - prefixSum[left + 1];
                maxProduct = Math.max(maxProduct, sum * nums[minIndex]);
            }
            stack.push(i);
        }
        return (int) (maxProduct % MOD);
    }
}

解释

方法:

这个题解采用了单调栈和前缀和的方法来求解。首先,使用单调栈来找到每个元素左侧第一个严格小于它的元素的位置和右侧第一个小于等于它的元素的位置。这样,对于每个元素,我们就可以确定一个区间,使得该元素是区间内的最小值。然后,使用前缀和数组来快速计算这些区间的元素和。最后,遍历每个元素,计算以它为最小值的区间的最小乘积,并更新最大值。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中提到使用单调栈找到每个元素左侧第一个严格小于它和右侧第一个小于等于它的元素的位置,这种方法对于找到这些位置的准确性如何保证?
单调栈用来有效地处理问题,关于数组中元素的“第一个小于或大于”这类问题。在本题中,使用一个单调递增栈,栈中存储元素的索引,确保从栈底到栈顶元素对应的数组值是递增的。当遇到一个新元素,如果它小于栈顶元素,栈顶元素就没有继续保持在栈中的意义,因为找到了右侧第一个小于等于它的元素,因此将栈顶元素弹出,并记录该位置。这样可以保证每一个元素弹出栈时,都正确地找到了右边第一个小于等于它的位置;同时,当前元素的左侧第一个严格小于它的元素就是此时栈顶的元素。这种方法因其严格的顺序控制和出栈时即时记录,可以确保位置的查找是准确的。
🦆
题解中的单调栈是如何工作的,能否详细解释一下为什么在遇到当前元素小于栈顶元素时,需要弹出栈顶元素?
单调栈在本题中是用来维护一个单调递增的序列,这是因为我们需要确定每个元素的左侧第一个严格小于它的元素和右侧第一个小于等于它的元素。当新加入的元素小于栈顶元素时,意味着栈顶元素的右侧第一个小于等于它的元素已经找到(即当前考察的这个新元素)。因此,我们需要弹出栈顶元素,并将当前元素的位置记录为栈顶元素的右侧边界。这个过程一直进行,直到栈顶元素小于当前元素或栈为空,确保栈的单调性。此外,当前元素的左侧第一个严格小于它的元素,就是在它入栈后的栈顶元素(如果栈不为空)。通过这种方式,单调栈可以有效地在一次遍历中解决问题,保证了效率。
🦆
题解中计算最小乘积时,最后返回的结果进行了取余操作,这里为什么要进行取余,直接返回最大值不可以吗?
在处理大数据问题时,直接返回结果可能会导致数值溢出,尤其是在计算乘积时,结果可能迅速增长到超出常规整数类型的存储范围。因此,为了防止溢出并保持结果的稳定性,通常在合适的模数下取余。在本题中,取余操作是为了确保结果不超过特定的数值范围,同时这也符合很多编程竞赛和实际应用中的要求,使用模 10^9+7 是因为它是一个大的质数,适合作为模运算的基数,有助于减少哈希冲突等问题。

相关问题