forked from greyireland/algorithm-pattern
-
Notifications
You must be signed in to change notification settings - Fork 3
/
stack_queue.md
547 lines (469 loc) · 14.5 KB
/
stack_queue.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# 栈和队列
## 简介
栈的特点是后入先出
![image.png](https://img.fuiboom.com/img/stack.png)
根据这个特点可以临时保存一些数据,之后用到依次再弹出来,常用于 DFS 深度搜索
队列一般常用于 BFS 广度搜索,类似一层一层的搜索
## Stack 栈
[min-stack](https://leetcode-cn.com/problems/min-stack/)
> 设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
思路:用两个栈实现,一个最小栈始终保证最小值在顶部
```c++
class MinStack {
private:
vector<int> valueStack;
vector<int> minStack;
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
valueStack.push_back(x);
auto curMin = getMin();
if (curMin < x) {
minStack.push_back(curMin);
} else {
minStack.push_back(x);
}
}
void pop() {
valueStack.pop_back();
minStack.pop_back();
}
int top() {
if (valueStack.empty()) {
return 0;
}
return valueStack.back();
}
int getMin() {
if (minStack.empty()) {
return numeric_limits<int>::max();
}
return minStack.back();
}
};
```
[evaluate-reverse-polish-notation](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
> **波兰表达式计算** > **输入:** ["2", "1", "+", "3", "*"] > **输出:** 9
> **解释:** ((2 + 1) \* 3) = 9
思路:通过栈保存原来的元素,遇到表达式弹出运算,再推入结果,重复这个过程
```c++
int evalRPN(vector<string> &tokens) {
if (tokens.empty()) {
return 0;
}
auto token = tokens.back();
tokens.pop_back();
if (token != "+" && token != "-" && token != "*" && token != "/") {
return atoi(token);
}
auto rhs = evalRPN(tokens);
auto lhs = evalRPN(tokens);
if (token == "+") {
return lhs + rhs;
} else if (token == "-") {
return lhs - rhs;
} else if (token == "*") {
return lhs * rhs;
} else if (token == "/") {
return lhs / rhs;
}
return -1;
}
int atoi(const string &str) {
if (str[0] == '-') {
return -atoi(str.substr(1));
}
int ret = 0;
for (const auto &item : str) {
ret = ret * 10 + item - '0';
}
return ret;
}
```
[decode-string](https://leetcode-cn.com/problems/decode-string/)
> 给定一个经过编码的字符串,返回它解码后的字符串。
> s = "3[a]2[bc]", 返回 "aaabcbc".
> s = "3[a2[c]]", 返回 "accaccacc".
> s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
思路:通过栈辅助进行操作
```c++
string decodeString(string s) {
if (s.empty()) {
return "";
}
vector<char> stack;
for (const auto &c : s) {
if (c != ']') {
stack.push_back(c);
continue;
}
vector<char> subStr;
while (stack.back() != '[') {
subStr.push_back(stack.back());
stack.pop_back();
}
stack.pop_back();
int digitBegin = stack.size() - 1;
for (; digitBegin != 0; --digitBegin) {
auto val = stack[digitBegin];
if (!('0' <= val && val <= '9')) {
++digitBegin;
break;
}
}
auto repeat = atoi({
stack.begin() + digitBegin,
stack.end(),
});
stack.resize(digitBegin);
for (int i = 0; i < repeat; ++i) {
stack.insert(stack.end(), subStr.rbegin(), subStr.rend());
}
}
return {
stack.begin(),
stack.end()
};
}
int atoi(const string &str) {
if (str.empty()) {
return 0;
}
int value = 0;
for (const auto &c : str) {
value = value * 10 + c - '0';
}
return value;
}
```
利用栈进行 DFS 递归搜索模板
```c++
bool DFS(Node root, Node target) {
set<Node> visited;
stack<Node> stack;
stack.push(root);
while (!stack.empty()) {
auto cur = stack.top();
return true if cur is target;
for (auto next : the neighbors of cur) {
if (visited.find(target) == visited.end() {
stack.push(next);
visited.insert(target);
}
}
stack.pop();
}
return false;
}
```
[binary-tree-inorder-traversal](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
> 给定一个二叉树,返回它的*中序*遍历。
```c++
// 思路:通过stack 保存已经访问的元素,用于原路返回
vector<int> inorderTraversal(TreeNode* root) {
if (!root) {
return {};
}
vector<int> ret;
vector<TreeNode*> stack;
while (root || !stack.empty()) {
while (root) {
// 从最左下节点开始
stack.push_back(root);
root = root->left;
}
root = stack.back();
stack.pop_back();
ret.push_back(root->val);
root = root->right;
}
return ret;
}
```
[clone-graph](https://leetcode-cn.com/problems/clone-graph/)
> 给你无向连通图中一个节点的引用,请你返回该图的深拷贝(克隆)。
```c++
Node* cloneGraph(Node* node) {
map<Node*, Node*> newNodes;
return clone(node, newNodes);
}
Node* clone(Node *node, map<Node*, Node*> &genNodes) {
if (!node) {
return nullptr;
}
if (genNodes.find(node) != genNodes.end()) {
return genNodes[node];
}
auto newNode = new Node(node->val);
genNodes[node] = newNode;
for (const auto &neighbor : node->neighbors) {
newNode->neighbors.push_back(clone(neighbor, genNodes));
}
return newNode;
}
```
[number-of-islands](https://leetcode-cn.com/problems/number-of-islands/)
> 给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
思路:通过深度搜索遍历可能性(注意标记已访问元素)
```c++
int numIslands(vector<vector<char>> &grid) {
auto num = 0;
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[i].size(); ++j) {
if (grid[i][j] == '1') {
flowTheIsland(grid, i, j);
++num;
}
}
}
return num;
}
void flowTheIsland(vector<vector<char>> &grid, int i, int j) {
if (i < 0 || i >= grid.size() || j < 0 || j >= grid[i].size() || grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
flowTheIsland(grid, i - 1, j);
flowTheIsland(grid, i + 1, j);
flowTheIsland(grid, i, j + 1);
flowTheIsland(grid, i, j - 1);
}
```
[largest-rectangle-in-histogram](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
> 给定 _n_ 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
> 求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路:求以当前柱子为高度的面积,即转化为寻找小于当前值的左右两边值
![image.png](https://img.fuiboom.com/img/stack_rain.png)
用栈保存小于当前值的左的元素
![image.png](https://img.fuiboom.com/img/stack_rain2.png)
```c++
#pragma region 暴力解法, 时间复杂度O(n^2)不满足
int largestRectangleArea0(vector<int> &heights) {
auto largest = 0;
for (int i = 0; i < heights.size(); ++i) {
auto area = calcArea(heights, i);
if (area > largest) {
largest = area;
}
}
return largest;
}
int calcArea(const vector<int> &heights, int col) {
auto height = heights[col];
auto begin = col;
while (begin > 0) {
if (heights[begin - 1] >= height) {
--begin;
} else {
break;
}
}
auto end = col;
while (end < heights.size() - 1) {
if (heights[end + 1] >= height) {
++end;
} else {
break;
}
}
return height * (end - begin + 1);
}
#pragma endregion 暴力解法
#pragma region 单调栈 + 哨兵
/* 若当前柱子的高度小于栈中元素,则弹出所有比它高的,并将当前柱子入栈
* 因为会被最低的挡住
* 结果就是栈中元素保持单调递增
* 一次循环保存结果避免嵌套循环么
*
* 使用“哨兵”来为左右边界占位,避免特殊处理
*
* 注意事项:
* 判断的时候要使用>=,弹出一样高度的柱子,以获得正确的边界!
*/
int largestRectangleArea1(vector<int> &heights) {
auto size = heights.size();
vector<int> left(size), right(size);
stack<int> monoStack;
for (int i = 0; i < size; ++i) {
while (!monoStack.empty() && heights[monoStack.top()] >= heights[i]) {
monoStack.pop();
}
left[i] = monoStack.empty() ? -1 : monoStack.top();
monoStack.push(i);
}
monoStack = {};
for (int i = size - 1; i >= 0; --i) {
while (!monoStack.empty() && heights[monoStack.top()] >= heights[i]) {
monoStack.pop();
}
right[i] = monoStack.empty() ? size : monoStack.top();
monoStack.push(i);
}
auto largest = 0;
for (int i = 0; i < size; ++i) {
auto area = (right[i] - left[i] - 1) * heights[i];
largest = largest > area ? largest : area;
}
return largest;
}
#pragma endregion
#pragma region 单调栈 + 常数优化
/*
* 少一次循环
*
* 在从左往右遍历时
* 入栈 = 左边界
* 出栈 = “右边界”
* 出栈,是>=,不是>,得到的右边界不准确?
* 没关系,对于同高度的区域,最右边的柱子能够得到准确的高度!以它为准即可
*/
int largestRectangleArea(vector<int> &heights) {
auto size = heights.size();
vector<int> left(size), right(size, size);
stack<int> monoStack;
for (int i = 0; i < size; ++i) {
while (!monoStack.empty() && heights[monoStack.top()] >= heights[i]) {
right[monoStack.top()] = i;
monoStack.pop();
}
left[i] = monoStack.empty() ? -1 : monoStack.top();
monoStack.push(i);
}
auto largest = 0;
for (int i = 0; i < size; ++i) {
auto area = (right[i] - left[i] - 1) * heights[i];
largest = largest > area ? largest : area;
}
return largest;
}
#pragma endregion
```
## Queue 队列
常用于 BFS 宽度优先搜索
[implement-queue-using-stacks](https://leetcode-cn.com/problems/implement-queue-using-stacks/)
> 使用栈实现队列
```c++
class MyQueue {
private:
vector<int> stack1;
vector<int> stack2;
public:
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
stack1.push_back(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
auto ret = peek();
stack2.pop_back();
return ret;
}
/** Get the front element. */
int peek() {
if (!stack2.empty()) {
return stack2.back();
}
stack2.insert(stack2.end(), stack1.rbegin(), stack1.rend());
stack1.clear();
return peek();
}
/** Returns whether the queue is empty. */
bool empty() {
return stack1.empty() && stack2.empty();
}
};
```
二叉树层次遍历
```c++
vector<vector<int>> levelOrder(TreeNode *root) {
vector<vector<int>> ret;
if (!root) {
return ret;
}
queue<TreeNode *> queue;
queue.push(root);
while (!queue.empty()) {
vector<int> levelValues;
for (auto levelNodeNum = queue.size(); levelNodeNum > 0; --levelNodeNum) {
auto node = queue.front();
queue.pop();
levelValues.push_back(node->val);
if (node->left) {
queue.push(node->left);
}
if (node->right) {
queue.push(node->right);
}
}
ret.push_back(levelValues);
}
return ret;
}
```
[01-matrix](https://leetcode-cn.com/problems/01-matrix/)
> 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。
> 两个相邻元素间的距离为 1
```c++
/*
* 从0出发,而不是从1出发!
* 把所有为0的节点压入队列,并进行广度优先遍历
* 并不是一个点往外不停扩散,再换下一个点(那是深度优先)
* 每次向外扩散一格,距离+1,并直接出栈!
* 剩下的交给外圈的继续扩散
*/
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int row = matrix.size(), column = matrix[0].size();
vector<vector<int>> dist(row, vector<int>(column));
vector<vector<bool>> seen(row, vector<bool>(column));
queue<pair<int, int>> q;
for (int i = 0; i < row; ++i) {
for (int j = 0; j < column; ++j) {
if (matrix[i][j] == 0) {
q.emplace(i, j);
seen[i][j] = 1;
}
}
}
int dirs[4][2] = {{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
while (!q.empty()) {
auto[i, j] = q.front();
q.pop();
for (auto &dir : dirs) {
int rowIdx = dir[0] + i;
int colIdx = dir[1] + j;
if (0 <= rowIdx && rowIdx < row
&& 0 <= colIdx && colIdx < column
&& !seen[rowIdx][colIdx]) {
dist[rowIdx][colIdx] = dist[i][j] + 1;
q.emplace(rowIdx, colIdx);
seen[rowIdx][colIdx] = true;
}
}
}
return dist;
}
```
## 总结
- 熟悉栈的使用场景
- 后入先出,保存临时值
- 利用栈 DFS 深度搜索
- 熟悉队列的使用场景
- 利用队列 BFS 广度搜索
## 练习
- [ ] [min-stack](https://leetcode-cn.com/problems/min-stack/)
- [ ] [evaluate-reverse-polish-notation](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/)
- [ ] [decode-string](https://leetcode-cn.com/problems/decode-string/)
- [ ] [binary-tree-inorder-traversal](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
- [ ] [clone-graph](https://leetcode-cn.com/problems/clone-graph/)
- [ ] [number-of-islands](https://leetcode-cn.com/problems/number-of-islands/)
- [ ] [largest-rectangle-in-histogram](https://leetcode-cn.com/problems/largest-rectangle-in-histogram/)
- [ ] [implement-queue-using-stacks](https://leetcode-cn.com/problems/implement-queue-using-stacks/)
- [ ] [01-matrix](https://leetcode-cn.com/problems/01-matrix/)