持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
LeetCode每日一题打卡专栏正式启动!不出意外将日更LeetCode的每日一题,敬请期待。
2104:子数组范围和
题意
给你一个整数数组 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
提示:
题解一:模拟
遍历所有子数组,保存对应的最小值和最大值,依次求子数组最大值和最小值之差的和即可。
C++代码:
class Solution {
public:
typedef long long ll;
int inf=0x3f3f3f3f;
long long subArrayRanges(vector<int>& nums) {
int n=nums.size();
int minnum=inf,maxnum=-inf;
ll ans=0;
for(int i=0;i<n;i++){
minnum=inf,maxnum=-inf;
for(int j=i;j<n;j++){
minnum=min(minnum,nums[j]);maxnum=max(maxnum,nums[j]);
// cout<<i<<"--"<<j<<"--"<<minnum<<"---"<<maxnum<<endl;
ans+=maxnum-minnum;
}
}
return ans;
}
};
题解二:单调栈
我们定义:i<j,如果nums[i]==nums[j],则逻辑上我们认为nums[i]<nums[j](因为i<j)。进行此定义为了方便处理单调栈。
题目所求为:所有子数组范围(子数组最大值-最小值)和。可以转化为所有子数组的最大值的和-所有子数组最小值和。
假设nums[j]左边第一个比它小的元素为nums[i],nums[j]右边第一个比它小的元素为nums[k]。则所有子数组中以nums[i]为最小值的个数为。如何获得nums[j]左边和右边第一个比它小的元素的下标呢, 单调栈刚好可以处理,预处理数组minL,minR。其中minL[i]表示nums[i]左边第一个比它小的元素的下标,minR[i]表示nums[i]右边第一个比它小的元素的下标。
单调栈的一个例子:
// 对于数组 [..., 3, 5,6,7,4,1,2] // // 要计算数字5的「右侧比5小的第一个数」的时候 // 需要关注的只有 [6,4,1] 这三个数,也就是单调栈。 // 由于6比5大,所以将6出栈,变成 [4,1],于是找到了,4就是「比5小的右侧第一个数」 // 然后将5入栈,变成 [5,4,1] // // 然后继续计算5左边的3的「右侧第一个更小的数」,此时需要考虑的栈是[5,4,1] // 依次将5, 4出栈,栈变成了[1],终于比3小了,1就是比3小的右侧第一个数。然后将3入栈,变成[3, 1],再继续往左。 // 即,计算「右侧比nums[i]小的第一个数」的时候,要从右往左算。于是我们以minL处理为例,从左到右遍历数组,当遍历到nums[i]时:
- 执行出栈,直到栈为空 或者 nums[minstack.top()]逻辑上小于nums[i],
- 如果栈为空,则minL[i]=-1,否则minL[i]=minstack.top()
- 然后将下标i入栈
于是所有子数组的最小值和为,同理求得maxsum。具体见代码:
C++代码:
class Solution {
public:
typedef long long ll;
long long subArrayRanges(vector<int>& nums) {
int n=nums.size();
stack<int> minstack,maxstack;
//minL[i]:表示nums[i]左边比它小的第一个元素的下标。其他同理
vector<int> minL(n),minR(n),maxL(n),maxR(n);
//处理minL[],maxL[]
for(int i=0;i<n;i++){
//如果当前栈顶元素大于nums[i],则出栈
while(!minstack.empty()&&nums[minstack.top()]>nums[i]) minstack.pop();
minL[i]=minstack.empty()?-1:minstack.top();
minstack.push(i);
//如果当前栈顶元素小于等于nums[i],则出栈
//上述已定义了排序规则,若nums[maxstack.top()]==nums[i],则nums[maxstack.top()]<nums[i](因为maxstack.top()<i)
while(!maxstack.empty()&&nums[maxstack.top()]<=nums[i]) maxstack.pop();
maxL[i]=maxstack.empty()?-1:maxstack.top();
maxstack.push(i);
}
while(!minstack.empty()) minstack.pop();
while(!maxstack.empty()) maxstack.pop();
//处理minR[],maxR[]
for(int i=n-1;i>=0;i--){
//如果当前栈顶元素大于等于nums[i],则出栈
//若nums[maxstack.top()]==nums[i],则nums[maxstack.top()]>nums[i](因为maxstack.top()>i)
while(!minstack.empty()&&nums[minstack.top()]>=nums[i]) minstack.pop();
minR[i]=minstack.empty()?n:minstack.top();
minstack.push(i);
//如果当前栈顶元素小于nums[i],则出栈
while(!maxstack.empty()&&nums[maxstack.top()]<nums[i]) maxstack.pop();
maxR[i]=maxstack.empty()?n:maxstack.top();
maxstack.push(i);
}
// for(int i=0;i<n;i++){
// cout<<minL[i]<<"---"<<minR[i]<<"---"<<maxL[i]<<"---"<<maxR[i]<<endl;
// }
ll maxsum=0,minsum=0;
for(int i=0;i<n;i++){
//分别以nums[i]为子数组的最大值或最小值的和,具体见题解描述
maxsum+=(ll)(maxR[i]-i)*(i-maxL[i])*nums[i];
minsum+=(ll)(minR[i]-i)*(i-minL[i])*nums[i];
}
return maxsum-minsum;
}
};
Java代码:
class Solution {
public long subArrayRanges(int[] nums) {
int n = nums.length;
int minL[] = new int[n];
int minR[] = new int[n];
int maxL[] = new int[n];
int maxR[] = new int[n];
// Java一般中Deque代替Stack和Queue
Deque<Integer> minstack = new ArrayDeque<>();
Deque<Integer> maxstack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!minstack.isEmpty() && nums[minstack.peek()] > nums[i])
minstack.pop();
minL[i] = minstack.isEmpty() ? -1 : minstack.peek();
minstack.push(i);
while (!maxstack.isEmpty() && nums[maxstack.peek()] <= nums[i])
maxstack.pop();
maxL[i] = maxstack.isEmpty() ? -1 : maxstack.peek();
maxstack.push(i);
}
minstack.clear();
maxstack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!minstack.isEmpty() && nums[minstack.peek()] >= nums[i])
minstack.pop();
minR[i] = minstack.isEmpty() ? n : minstack.peek();
minstack.push(i);
while (!maxstack.isEmpty() && nums[maxstack.peek()] < nums[i])
maxstack.pop();
maxR[i] = maxstack.isEmpty() ? n : maxstack.peek();
maxstack.push(i);
}
long minsum = 0, maxsum = 0;
for (int i = 0; i < n; i++) {
minsum += (long)(i - minL[i]) * (minR[i] - i) * nums[i];
maxsum += (long)(i - maxL[i]) * (maxR[i] - i) * nums[i];
}
return maxsum - minsum;
}
}