Skip to content

Commit b7b9e33

Browse files
committed
add LeetCode 907. 子数组的最小值之和
1 parent e61784b commit b7b9e33

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL2Nob2NvbGF0ZTE5OTkvY2RuL2ltZy8yMDIwMDgyODE0NTUyMS5qcGc?x-oss-process=image/format,png)
2+
>仰望星空的人,不应该被嘲笑
3+
4+
## 题目描述
5+
6+
给定一个整数数组 A,找到 `min(B)` 的总和,其中 B 的范围为 A 的每个(连续)子数组。
7+
8+
由于答案可能很大,因此返回答案模 `10^9 + 7`
9+
10+
11+
12+
示例:
13+
14+
```javascript
15+
输入:[3,1,2,4]
16+
输出:17
17+
解释:
18+
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
19+
最小值为 3124112111,和为 17
20+
```
21+
22+
23+
24+
提示:
25+
26+
```javascript
27+
1 <= A <= 30000
28+
1 <= A[i] <= 30000
29+
```
30+
31+
来源:力扣(LeetCode)
32+
链接:https://leetcode-cn.com/problems/sum-of-subarray-minimums
33+
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
34+
35+
## 解题思路
36+
搬运 `jack-108`大佬的<a href="https://leetcode-cn.com/problems/sum-of-subarray-minimums/solution/guan-fang-er-jie-fa-javascript-xiang-jie-by-jack-1/">题解</a>:
37+
38+
既然是求子数组中最小值的和,就是求 以 A[i] 作为最小数能构成的数组有多少个。
39+
40+
比如 `[2,4,1,2]` ,以`1` 为最小数。能构成的数组数为 `(2+1)*(1+1)``2` 表示 `1 `左面有两个比它大的数,`1` 表示 `1` 右面有一个比它大的数。
41+
42+
用单调栈求出 `A[i]` 对应的左右最近比 `A[i]` 小的数,记索引为 `prev,next,A[i] `为最小数能形成的数组为
43+
44+
```javascript
45+
(i-prev[i])*(next[i]-i)
46+
```
47+
48+
这里为什么没有加 `1` 了呢,因为 `prev[i]`已经是比 `A[i]` 小的数了,能和 `A[i]` 形成数组的都是比 `A[i]` 大的数。
49+
50+
**我的解题方式:**
51+
52+
注释已经足够详细,还是补充一下,我参考了大佬的解题代码,只不过我是直接求出来了以当前 `A[i]` 为最小值的子数组左右两边 大于或等于当前值的个数。这样后续求和直接相乘即可。(不过这里要**强调一下**,如果左边设置大于等于了,右边就只能是大于了,不然会重复计算相等的值)
53+
54+
开始有点看不懂大佬为啥左边初始化为 `-1`,右边初始化为 `A.length` 。假如我们遇到了这种情况,左边都比当前 `A[i]` 要大,那我们维护的单调递减栈就会不断出栈,不断出栈,直到栈为空为止,此时左边个数应该为 `i+1`(从 0 开始计数嘛),那么这部分大佬设为 `-1` 就很巧妙了,问题也就自然明白啦,个人感觉自己写的还是比较好理解一点,不然直接弄一个 `-1` ,第一次用单调栈,还是不太熟...
55+
56+
那么对于右边初始化为 `A.length` ,也是同理啦,当右边都比当前 `A[i]` 要大,那我们维护的单调递减栈就会不断出栈,不断出栈,直到栈为空为止,此时右边个数应该为 `A.length-i`(不用`+1`的原因是从0计数),那么这部分大佬设为 `A.length` 就很巧妙了,依旧清晰明了。
57+
58+
59+
```javascript
60+
/**
61+
* @param {number[]} A
62+
* @return {number}
63+
*/
64+
var sumSubarrayMins = function(A) {
65+
let mod = 1e9+7
66+
// 维护一个栈
67+
let stack = []
68+
// 求以A[i]为最小值的子数组左边大于或等于自己的个数
69+
let prev = []
70+
for(let i=0;i<A.length;i++){
71+
while(stack.length && A[stack[stack.length-1]] >= A[i]) stack.pop()
72+
// 如果栈为空,即左边都比自己大,则返回i+1,否则返回i-栈顶元素(即保存的下标值)
73+
prev[i] = stack.length ? i - stack[stack.length-1] : i+1
74+
stack.push(i)
75+
}
76+
stack = []
77+
// 求以A[i]为最小值的子数组右边大于自己的个数(没有等号是因为不会重复计算相等的值)
78+
let nextv = []
79+
for(let i=A.length-1;i>=0;i--){
80+
while(stack.length && A[stack[stack.length-1]] > A[i]) stack.pop()
81+
// 如果栈为空,即右边都比自己大,则返回A.length-i,否则返回栈顶元素(即保存的下标值)-i
82+
nextv[i] = stack.length? stack[stack.length-1] - i : A.length-i
83+
stack.push(i)
84+
}
85+
let sum = 0
86+
for(let i=0;i<A.length;i++){
87+
// 以A[i] 为最小值的子数组的组合共有prev[i]*nextv[i]种情况,那么和的话乘以A[i]累加即可
88+
sum += (prev[i]*nextv[i]*A[i])
89+
// 按题意,进行取模运算
90+
sum %= mod
91+
}
92+
return sum
93+
};
94+
```
95+
96+
97+
## 最后
98+
文章产出不易,还望各位小伙伴们支持一波!
99+
100+
往期精选:
101+
102+
<a href="https://github.com/Chocolate1999/Front-end-learning-to-organize-notes">小狮子前端の笔记仓库</a>
103+
104+
<a href="https://yangchaoyi.vip/">访问超逸の博客</a>,方便小伙伴阅读玩耍~
105+
106+
![](https://img-blog.csdnimg.cn/2020090211491121.png#pic_center)
107+
108+
```javascript
109+
学如逆水行舟,不进则退
110+
```
111+

0 commit comments

Comments
 (0)