907. 子数组的最小值之和

题目描述

给定一个整数数组 leet code 907. 子数组的最小值之和_子数组
找到 min(b) 的总和
其中 b 的范围为 arr 的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 leet code 907. 子数组的最小值之和_最小值_02

示例 1:

输入:arr = [3,1,2,4] 输出:17解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]
最小值为 3,1,2,4,1,1,2,1,1,1和为 17

示例 2:

输入:arr = [11,81,94,43,3] 输出:444

提示:
leet code 907. 子数组的最小值之和_数组_03
leet code 907. 子数组的最小值之和_数组_04

题目解析

  • 直观地,遍历拿到每一个子数组的最小值然后相加得到最终结果
  • 很显然这样时间复杂度太高了
  • 由于只需要求每一个连续子数组的最小值,每一个元素值考虑和其后面的元素相比较即可

  1. 以数组 arr = [3, 1, 2, 4] 为例,有四个元素
  2. 以元素3为中心的话,选取子数组,保证元素3最小,则一共有一种方案 ,即{3}
  1. 因为元素3最小的子数组有且仅有{3}
  1. 以元素1为最小值的子数组的最长连续子数组是 {3, 1, 2, 4}
  1. 所有的子数组,元素1都是最小值
  2. 和 = 子数组个数 * 1
  3. 那么问题变成了如何计算子数组个数
  4. 所有的子数组有 {3,1},{3,1,2},{3,1,2,4},{1},{1,2},{1,2,4} 一共有 6
  5. 可以得到左右边界以及当前元素的下标
  6. 因此有:子数组个数 = (当前元素下标 - 左边界下标 + 1)* (右边界下标 - 当前元素下标 + 1)
  1. 记当前元素下标为
  1. leet code 907. 子数组的最小值之和_数组_05为最小值的左边界下标为leet code 907. 子数组的最小值之和_最小值_06
  2. leet code 907. 子数组的最小值之和_数组_05为最小值的右边界下标为leet code 907. 子数组的最小值之和_子数组_08
  3. leet code 907. 子数组的最小值之和_最小值_09

show code

class Solution {
    public int sumSubarrayMins(int[] arr) {
        int n = arr.length;
        // 创建一个数组  代表以当前元素为最小元素的左边界范围
        int[] left = new int[n];
        // 右边界数组
        int[] right = new int[n];
        // 创建一个栈辅助找到最小值
        Deque<Integer> stack = new LinkedList<>();
        for(int i = 0;i < n;i++) {
            // 这里用大于 等于 判断:表示左边界去包含相等元素
            while(!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
                stack.pop();
            }
            // 计算左边界的长度范围
            left[i] = i - (stack.isEmpty()?-1:stack.peek());
            stack.push(i);
        }
        stack.clear();
        for(int i = n - 1;i >= 0;i--) {
            // 右边界不包含 相等元素
            while(!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
                stack.pop();
            }
            // 计算右边界范围
            right[i] = (stack.isEmpty()?n:stack.peek()) - i;
            stack.push(i);
        }

        long ans = 0;
        int mod = 1000000007;
        // 计算最终结果.
        for(int i = 0;i < n;i++) {
            ans = (ans + (long)left[i] * right[i] * arr[i]) % mod;
        }
        return (int) ans;
    }
}