给你一个整数数组 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
题目来源:LeetCode2104.数组范围和
思路
单调栈
常规思路是枚举所有区间内的最大值和最小值后进行计算
也可以反过来想,枚举所有元素分别作为最大值和最小值出现的区间的个数
接下来只需求出离每个元素左边和右边最近的最大值和最小值
是单调栈的常规应用
设数
n
u
m
s
[
i
]
nums[i]
nums[i] 是下标为
i
i
i 的数
其左边最大的数的下标为
l
[
i
]
l[i]
l[i],右边最大的数的下表为
r
[
i
]
r[i]
r[i]
则
n
u
m
s
[
i
]
nums[i]
nums[i] 是区间
[
l
[
i
]
+
1
,
r
[
i
]
−
1
]
[l[i] + 1, r[i] - 1]
[l[i]+1,r[i]−1] 内的最大值
使用计数原理,包含
n
u
m
s
[
i
]
nums[i]
nums[i] 的连续子区间的个数为
(
i
−
l
[
i
]
)
∗
(
r
[
i
]
−
i
)
(i - l[i])*(r[i] - i)
(i−l[i])∗(r[i]−i)
最小值同理
另外需要注意的是,数组的可能存在相同的元素
故求两端最近的最值时,其中一端的条件应该弱一些
如左边求严格大于的值,右边求大于等于的值
举例:
[
6
,
4
,
4
,
2
,
8
]
[6,4,4,2,8]
[6,4,4,2,8]
对元素
4
4
4 来说:
- 若条件是两端都求严格大于 4 4 4 的数,则两个元素 4 4 4 都是区间 [ 1 , 3 ] [1,3] [1,3] 之间的最大值,会重复计算
- 若使右端条件变弱,第一个 4 4 4 是区间 [ 1 , 1 ] [1,1] [1,1] 内的最大值,第二个 4 4 4 是区间 [ 2 , 3 ] [2,3] [2,3] 内的最大值,不会重复计算
代码
class Solution {
public:
long long subArrayRanges(vector<int>& nums) {
int n = nums.size();
long long ans = 0;
vector<int> l1(n), r1(n), l2(n), r2(n); //分别表示左最大、右大于等于、左最小、右小于等于的数的下标
stack<int> sl1, sr1, sl2, sr2;
for (int i = 0; i < n; i ++ ) {
while (sl1.size() && nums[i] >= nums[sl1.top()]) {
sl1.pop();
}
if (sl1.empty()) l1[i] = -1; //不存在时为-1
else l1[i] = sl1.top();
sl1.push(i);
while (sl2.size() && nums[i] <= nums[sl2.top()]) {
sl2.pop();
}
if (sl2.empty()) l2[i] = -1;
else l2[i] = sl2.top();
sl2.push(i);
}
for (int i = n - 1; i >= 0; i -- ) {
while (sr1.size() && nums[i] > nums[sr1.top()]) {
sr1.pop();
}
if (sr1.empty()) r1[i] = n; //不存在时为n
else r1[i] = sr1.top();
sr1.push(i);
while (sr2.size() && nums[i] < nums[sr2.top()]) {
sr2.pop();
}
if (sr2.empty()) r2[i] = n;
else r2[i] = sr2.top();
sr2.push(i);
}
for (int i = 0; i < n; i ++ ) {
ans += (long long)nums[i] * (i - l1[i]) * (r1[i] - i); //加上最大值
ans -= (long long)nums[i] * (i - l2[i]) * (r2[i] - i); //减去最小值
}
return ans;
}
};