Skip to content

Commit ad514e3

Browse files
committed
add monotonic stack & queue 🍺
1 parent 69b3f60 commit ad514e3

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

Diff for: algorithm/README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
## 数组
88
* [排序](array/sort.md)
99
* 快速排序
10-
* 归并排序
10+
* 归并排序
11+
12+
* [单调数据结构](array/monotonic_stack_queue.md)
13+
* 单调栈
14+
* 单调队列
1115

1216
##
1317

Diff for: algorithm/array/monotonic_stack_queue.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# 单调数据结构
2+
3+
## 1. 单调栈
4+
5+
### 1.1 原理
6+
7+
顾名思义,单调栈即元素单调递增或递减排列的栈。我们可以**通过单调栈求元素的左(右)边第一个比他大(小)的元素**
8+
9+
例如我们想求数组里每个元素的左边第一个比它小的元素,我们可以维护一个严格单调增的栈,我们从前往后遍历原数组,设当前元素为num:
10+
11+
* 我们不断去掉栈顶元素直到栈空或者栈顶元素小于num,此时栈顶元素就是num的左边第一个比它小的元素,记录结果,然后将num入栈。
12+
13+
整个过程的复杂度为 O(n) ,因为所有元素只会进栈出栈一次。
14+
15+
### 1.2 代码
16+
17+
``` C++
18+
vector<int> PreviousSmallerElement(vector<int>& nums) {
19+
/*
20+
求数组里每个元素的左边第一个比它小的元素,若找不到用-1填充
21+
*/
22+
vector<int> res(nums.size());
23+
stack<int> monoStack;
24+
for (int i = 0; i < nums.size(); i++) {
25+
while (!monoStack.empty() && monoStack.top() >= nums[i])
26+
monoStack.pop();
27+
28+
res[i] = monoStack.empty() ? -1 : monoStack.top();
29+
monoStack.push(nums[i]);
30+
}
31+
return res;
32+
}
33+
```
34+
35+
### 1.3 相关题目
36+
37+
LeetCode上面可以用单调栈解决的题还挺多的:
38+
* [42. Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/)
39+
* [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/)
40+
* [85. Maximal Rectangle](https://leetcode.com/problems/maximal-rectangle/)
41+
* [456. 132 Pattern](https://leetcode.com/problems/132-pattern/)
42+
* [496. Next Greater Element I](https://leetcode.com/problems/next-greater-element-i/)
43+
* [503. Next Greater Element II](https://leetcode.com/problems/next-greater-element-ii/)
44+
* [739. Daily Temperatures](https://leetcode.com/problems/daily-temperatures/)
45+
* [901. Online Stock Span](https://leetcode.com/problems/online-stock-span/)
46+
47+
48+
## 2. 单调队列
49+
50+
### 2.1 原理
51+
52+
与单调栈类似,单调队列即元素单调递增或递减排列的队列。我们可以**通过单调队列求滑动窗口的最值问题**。一般用双向队列`deque`实现。
53+
54+
55+
例如我们想求大小为k的滑动窗口内部的最大值(LeetCode239),那么核心思路就是用一个`deque`存放有可能成为最大值的元素(的**下标**)。从左往右滑动窗口的过程中,若窗口即将把`nums[i]`包含进来,
56+
57+
1. 首先,若队首元素下标小于`i - k`,即队首在窗口之外了,所以应该删除队首元素;
58+
2. 然后,由于我们仅保留有可能成为最大值的元素(的下标),所以我们应该从队尾开始不断去掉比`nums[i]`小的那些元素(的下标),因为只要窗口内有`nums[i]`,那么去掉的这些元素就不可能成为最大值。
59+
3. 最后,我们将`nums[i]`(的下标)送入队尾。
60+
61+
因此,按照上述过程维护的队列里面的元素是单调递减的,队首的元素即每次窗口内的最大值。
62+
63+
每个元素入队出队一次,时间复杂度O(n),且仅遍历一次数组。
64+
65+
> 滑动窗口最值问题还有一个同样O(n)的思路,具体见[239题解](../../solutions/239.%20Sliding%20Window%20Maximum.md)思路二。
66+
67+
### 2.2 代码
68+
``` C++
69+
// Leetcode 239
70+
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
71+
vector<int>res;
72+
deque<int>win;
73+
74+
for(int i = 0; i < nums.size(); i++){
75+
if (!win.empty() && win.front() == i - k) win.pop_front();
76+
77+
while(!win.empty() && nums[win.back()] <= nums[i]) win.pop_back();
78+
79+
win.push_back(i);
80+
81+
if(i >= k - 1) res.push_back(nums[win.front()]);
82+
}
83+
return res;
84+
}
85+
```
86+
87+
### 2.3 相关题目
88+
89+
* [239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/)
90+
91+
92+

0 commit comments

Comments
 (0)