LeetCode-2104 子数组范围和

简介: LeetCode-2104 子数组范围和

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/sum-of-subarray-ranges

题目描述

给你一个整数数组 nums 。nums 中,子数组的 范围 是子数组中最大元素和最小元素的差值。

返回 nums 中 所有 子数组范围的 和 。

子数组是数组中一个连续 非空 的元素序列。

 

示例 1:
输入:nums = [1,2,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[2],范围 = 2 - 2 = 0
[3],范围 = 3 - 3 = 0
[1,2],范围 = 2 - 1 = 1
[2,3],范围 = 3 - 2 = 1
[1,2,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 1 + 1 + 2 = 4
示例 2:
输入:nums = [1,3,3]
输出:4
解释:nums 的 6 个子数组如下所示:
[1],范围 = 最大 - 最小 = 1 - 1 = 0
[3],范围 = 3 - 3 = 0
[3],范围 = 3 - 3 = 0
[1,3],范围 = 3 - 1 = 2
[3,3],范围 = 3 - 3 = 0
[1,3,3],范围 = 3 - 1 = 2
所有范围的和是 0 + 0 + 0 + 2 + 0 + 2 = 4
示例 3:
输入:nums = [4,-2,-3,4,1]
输出:59
解释:nums 中所有子数组范围的和是 59
提示:
1 <= nums.length <= 1000
-109 <= nums[i] <= 109

 

解题思路

首先看数据范围,可以通过暴力法来做,遍历子数组,分别求出最大值最小值然后求和,时间复杂度是O(n2)

还有一种巧妙的方法可以将时间复杂度压缩到O(n)。

对于第i个数ai,如果左边第一个比他小的数下标为left,第一个比他小的数下标位right,那么(left,right)中所有的子数组最小值都是ai,在(left,right)中共有(right - i) * (i - left) 个子数组,那么(left, right)范围内子数组最小值的和为(right - i) * (i - left) * ai,同理可以求出(left, right)范围内子数组最大值的和,两个相减就可以求出(left, right)范围内的范围和。

问题转化为了如何第i个数左边小值和大值及右边的小值和大值,使用单调栈一次遍历便可以分别求得这四个值,并且用vector将下标存起来。

代码展示

暴力法:

 

class Solution {
public:
    long long subArrayRanges(vector<int>& nums) {
        int n = nums.size();
        long long ret = 0;
        for (int i = 0; i < n; i++) {
            int minVal = INT_MAX, maxVal = INT_MIN;
            for (int j = i; j < n; j++) {
                minVal = min(minVal, nums[j]);
                maxVal = max(maxVal, nums[j]);
                ret += maxVal - minVal;
            }
        }
        return ret;
    }
};

 

单调栈+数学:

 

class Solution {
public:
    long long subArrayRanges(vector<int>& nums) {
        int n = nums.size();
        long long ret = 0;
        vector<int> viLeftMin(n), viRightMin(n), viLeftMax(n), viRightMax(n);
        stack<int> siMax, siMin;
        for(int i = 0; i < n; i++)
        {
            while(!siMin.empty() && nums[siMin.top()] > nums[i])
                siMin.pop();
            viLeftMin[i] = siMin.empty()? -1: siMin.top();
            siMin.push(i);
            while(!siMax.empty() && nums[siMax.top()] <= nums[i])
                siMax.pop();
            viLeftMax[i] = siMax.empty()? -1: siMax.top();
            siMax.push(i);
        }
        siMax = stack<int>();
        siMin = stack<int>();
        for(int i = n - 1; i >= 0; i--)
        {
            while(!siMin.empty() && nums[siMin.top()] >= nums[i])
                siMin.pop();
            viRightMin[i] = siMin.empty()? n: siMin.top();
            siMin.push(i);
            while(!siMax.empty() && nums[siMax.top()] < nums[i])
                siMax.pop();
            viRightMax[i] = siMax.empty()? n: siMax.top();
            siMax.push(i);
        }
        for(int i = 0; i < n; i++)
        {
            ret += ((((long long)viRightMax[i] - i) * (i - viLeftMax[i])) - (((long long)viRightMin[i] - i) * (i - viLeftMin[i])))* nums[i];
        }
        return ret;
    }
};

运行结果

 

相关文章
|
23天前
|
存储 算法
LeetCode刷题---209. 长度最小的子数组(双指针-滑动窗口)
LeetCode刷题---209. 长度最小的子数组(双指针-滑动窗口)
|
23天前
|
Go
golang力扣leetcode 713.乘积小于K的子数组
golang力扣leetcode 713.乘积小于K的子数组
21 0
|
23天前
|
Java C++ Python
leetcode-209:长度最小的子数组
leetcode-209:长度最小的子数组
28 0
|
23天前
|
索引
leetcode代码记录(最长重复子数组
leetcode代码记录(最长重复子数组
15 0
|
23天前
leetcode代码记录(长度最小的子数组
leetcode代码记录(长度最小的子数组
17 0
|
23天前
【力扣】209. 长度最小的子数组
【力扣】209. 长度最小的子数组
|
23天前
|
算法 测试技术
每日一题:LeetCode-209. 长度最小的子数组(滑动窗口)
每日一题:LeetCode-209. 长度最小的子数组(滑动窗口)
|
23天前
|
算法
LeetCode题:581. 最短无序连续子数组,242. 有效的字母异位词,202. 快乐数
LeetCode题:581. 最短无序连续子数组,242. 有效的字母异位词,202. 快乐数
32 0
|
23天前
代码随想录 Day44 动规12 LeetCode T300 最长递增子序列 T674 最长连续递增序列 T718 最长重复子数组
代码随想录 Day44 动规12 LeetCode T300 最长递增子序列 T674 最长连续递增序列 T718 最长重复子数组
46 0
|
23天前
|
存储
【Leetcode 209】长度最小的子数组 —— 滑动窗口|双指针
我们可以使用双指针解决本题,定义两个指针 i 和 j 分别表示子数组(滑动窗口窗口)的开始位置和结束位置,维护变量 sum 存储子数组中的元素和。每一轮迭代中,每当 sum >= target 则记录子数组最小长度,移动慢指针。在每一轮迭代最后,移动快指针