题目
给你一个整数数组 nums 。nums 中,子数组的 范围 是子数组中最大元素和最小元素的差值。
返回 nums 中 所有 子数组范围的 和 。
子数组是数组中一个连续 非空 的元素序列。
链接:https://leetcode-cn.com/problems/sum-of-subarray-ranges
思路
方法一:朴素思路,二重循环枚举每个子数组
方法二:单调栈
题目转化
问题转成求解所有区间的最大值的和减去所有区间的最小值的和
进一步的考虑每一个元素作为区间的最大值的区间个数,作为区间的最小值的区间个数
使用简单的例子去理解
防止有相等元素,所以从左向右使用>=和从右向左<
单调栈直观的理解是能处理的元素,就处理,不能处理的元素存入栈中,稍后处理
代码
class Solution {
public:
long long subArrayRanges(vector<int>& nums) {
int n=nums.size();
vector<int> left_s(n,0); // 记录索引
vector<int> right_s(n,0);
vector<int> left_l(n,0);
vector<int> right_l(n,0);
stack<int> s; // 记录索引
s.push(-1);
// 单调递增栈,从左向右
for(int i=0;i<n;i++){
while(s.top()!=-1 && nums[s.top()]>=nums[i]) // 栈中的元素始终有序
s.pop(); // 不满足条件,出栈
left_s[i]=s.top();
s.push(i); // 满足条件,入栈
}
s.empty();
// 单调递增栈,从右向左
s.push(n);
for(int i=n-1;i>=0;i--){
while(s.top() != n && nums[s.top()]>nums[i])
s.pop();
right_s[i]=s.top();
s.push(i);
}
s.empty();
// 单调递减栈,从左向右
s.push(-1);
for(int i=0;i<n;i++){
while(s.top()!=-1 && nums[s.top()]<=nums[i])
s.pop();
left_l[i]=s.top();
s.push(i);
}
s.empty();
// 单调递减栈,从右向左
s.push(n);
for(int i=n-1;i>=0;i--){
while(s.top() !=n && nums[s.top()]<nums[i])
s.pop();
right_l[i]=s.top();
s.push(i);
}
s.empty();
long long ans=0;
for(int i=0;i<n;i++){
ans += 1L*nums[i]*(right_l[i]-i)*(i-left_l[i]);
ans -= 1L*nums[i]*(right_s[i]-i)*(i-left_s[i]);
}
return ans;
}
};