Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e2d43f7

Browse files
add monotonous stack
1 parent 2324fc6 commit e2d43f7

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

‎README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- [滑动窗口思想](https://github.com/greyireland/algorithm-pattern/blob/master/advanced_algorithm/slide_window.md)
4141
- [二叉搜索树](https://github.com/greyireland/algorithm-pattern/blob/master/advanced_algorithm/binary_search_tree.md)
4242
- [回溯法](https://github.com/greyireland/algorithm-pattern/blob/master/advanced_algorithm/backtrack.md)
43+
- [单调栈](./advanced_algorithm/monotonous_stack.md)
4344

4445
## 心得体会
4546

‎advanced_algorithm/monotonous_stack.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# 单调栈
2+
3+
## 介绍
4+
5+
简单来讲单调栈就是里面所有值都是单调递增或者递减的栈,这里先拿递增来举例说明,下面是一个典型的单调递增栈的模板:
6+
7+
```
8+
for(int i = 0; i < A.size(); i++){
9+
while(!in_stk.empty() && in_stk.top() > A[i]){
10+
in_stk.pop();
11+
}
12+
in_stk.push(A[i]);
13+
}
14+
```
15+
16+
这种数据结构能干什么呢?想象下如果有一个数组, `[3, 7, 8, 4]`,要在 O(N) 的时间内找到每个元素前面或者后面的最小值该怎么去做?这就是单调栈的作用
17+
18+
- 找到元素前面的最小值,`PLE`(Previous Less Element)。
19+
20+
以上面的数组为例:
21+
22+
7 的 PLE 为 3,
23+
24+
8 的 PLE 为 7,
25+
26+
4 的 PLE 为 3,
27+
28+
如果我们不直接把值塞进去,而是把数组下表扔进栈,那么我们就可以获取到一个新的 PLE 数组。如下:
29+
```
30+
// previous_less[i] = j means A[j] is the previous less element of A[i].
31+
// previous_less[i] = -1 means there is no previous less element of A[i].
32+
vector<int> previous_less(A.size(), -1);
33+
for(int i = 0; i < A.size(); i++){
34+
while(!in_stk.empty() && A[in_stk.top()] > A[i]){
35+
in_stk.pop();
36+
}
37+
previous_less[i] = in_stk.empty()? -1: in_stk.top();
38+
in_stk.push(i);
39+
}
40+
```
41+
42+
- 找到元素后面的最小值,`NLE`(Next Less Element)
43+
44+
同理可以获取到下面的模板
45+
46+
```
47+
// next_less[i] = j means A[j] is the next less element of A[i].
48+
// next_less[i] = -1 means there is no next less element of A[i].
49+
vector<int> next_less(A.size(), -1);
50+
for(int i = 0; i < A.size(); i++){
51+
while(!in_stk.empty() && A[in_stk.top()] > A[i]){
52+
auto x = in_stk.top();
53+
in_stk.pop();
54+
next_less[x] = i;
55+
}
56+
in_stk.push(i);
57+
}
58+
```
59+
60+
## 应用
61+
62+
[sum-of-subarray-minimums](https://leetcode-cn.com/problems/sum-of-subarray-minimums/)
63+
64+
> 子数组的最小值之和:给定一个整数数组 A,找到 min(B) 的总和,其中 B 的范围为 A 的每个(连续)子数组。
65+
66+
利用上述方法求出某个元素距离 `PLE``NLE` 之间的距离,然后对 `A[i]*left[i]*right[i]` 进行累加。
67+
```go
68+
func sumSubarrayMins(A []int) int {
69+
mod := int(1e9+7)
70+
stack := make([]int, 0, len(A))
71+
left, right := make([]int, len(A)), make([]int, len(A))
72+
73+
// 初始化 防止最小值没有处理的情况
74+
for i := 0; i < len(A); i += 1 {
75+
left[i] = i + 1
76+
right[i] = len(A)-i
77+
}
78+
79+
for i := 0; i < len(A); i += 1 {
80+
for len(stack) != 0 && A[stack[len(stack)-1]] > A[i] {
81+
last := stack[len(stack)-1]
82+
right[last] = i - last
83+
stack = stack[:len(stack)-1]
84+
}
85+
if len(stack) != 0 {
86+
left[i] = i - stack[len(stack)-1]
87+
}
88+
stack = append(stack, i)
89+
}
90+
// 累加
91+
var sum int
92+
for i := 0; i < len(A); i += 1 {
93+
sum = (sum + A[i]*left[i]*right[i]) % mod
94+
}
95+
return sum
96+
}
97+
```
98+
99+
[next-greater-element-ii](https://leetcode-cn.com/problems/next-greater-element-ii/)
100+
101+
> 下一个更大元素 II:给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
102+
103+
这是一个很基础的单调递减栈,主要的问题在于这个是一个循环数组,所以需要遍历两次,第二次遍历的时候不再入栈.
104+
```go
105+
func nextGreaterElements(nums []int) []int {
106+
if len(nums) == 0 {
107+
return nil
108+
}
109+
stack, ret := make([]int, 0, len(nums)), make([]int, len(nums))
110+
// 第一遍遍历初始化
111+
for i := range ret {
112+
ret[i] = -1
113+
}
114+
// 这里会遍历两遍,只有第一次遍历的时候数据才会入栈
115+
for i := 0; i < len(nums) * 2; i += 1 {
116+
117+
i := i % len(nums)
118+
for len(stack) != 0 && nums[stack[len(stack)-1]] < nums[i] {
119+
ret[stack[len(stack)-1]] = nums[i]
120+
stack = stack[:len(stack)-1]
121+
}
122+
123+
if i < len(nums) {
124+
stack = append(stack, i)
125+
}
126+
127+
}
128+
return ret
129+
}
130+
```
131+
132+
[largest-rectangle-in-histogram](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
133+
134+
> 给定 _n_ 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
135+
> 求在该柱状图中,能够勾勒出来的矩形的最大面积
136+
137+
[栈的练习](../data_structure/stack_queue.md)中已经出现过了
138+
139+
## 练习
140+
141+
- [ ] [remove-k-digits](https://leetcode-cn.com/problems/remove-k-digits/)
142+
- [ ] [maximal-rectangle](https://leetcode-cn.com/problems/maximal-rectangle/)
143+
- [ ] [trapping-rain-water](https://leetcode-cn.com/problems/trapping-rain-water/)(challenge)
144+
- [ ] [remove-duplicate-letters](https://leetcode-cn.com/problems/remove-duplicate-letters/)(challenge)
145+
- [ ] [create-maximum-number](https://leetcode-cn.com/problems/create-maximum-number/)
146+
- [ ] [max-chunks-to-make-sorted-ii](https://leetcode-cn.com/problems/max-chunks-to-make-sorted-II/)
147+
- [ ] [sliding-window-maximum](https://leetcode-cn.com/problems/sliding-window-maximum/)(challenge)
148+
149+
## Ref:
150+
- https://leetcode.com/problems/sum-of-subarray-minimums/discuss/178876/stack-solution-with-very-detailed-explanation-step-by-step

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /