Skip to content

Commit 38072d2

Browse files
committed
🎨 update: stack and queue
1 parent e61a987 commit 38072d2

File tree

5 files changed

+261
-0
lines changed

5 files changed

+261
-0
lines changed

docs/.vuepress/config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ module.exports = {
7777
'/dataStructure/堆/最小的k个数',
7878
]
7979
},
80+
{
81+
title: '栈和队列',
82+
children: [
83+
'/dataStructure/栈和队列/包含min函数的栈',
84+
'/dataStructure/栈和队列/滑动窗口的最大值',
85+
'/dataStructure/栈和队列/用两个栈实现队列',
86+
'/dataStructure/栈和队列/栈的压入弹出序列',
87+
]
88+
},
8089
]
8190
}
8291
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
{
3+
"title": "包含min函数的栈",
4+
}
5+
---
6+
7+
## 包含min函数的栈
8+
9+
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
10+
11+
## 思路
12+
13+
1.定义两个栈,一个栈用于存储数据,另一个栈用于存储每次数据进栈时栈的最小值.
14+
15+
2.每次数据进栈时,将此数据和最小值栈的栈顶元素比较,将二者比较的较小值再次存入最小值栈.
16+
17+
4.数据栈出栈,最小值栈也出栈。
18+
19+
3.这样最小值栈的栈顶永远是当前栈的最小值。
20+
21+
以数据[3,4,2,7,9,0]为例,让这组数字依次如栈,则栈和其对应的最小值栈如下:
22+
23+
![](../../dist/img/mainstack.png)
24+
25+
## 代码
26+
27+
```js
28+
var dataStack = [];
29+
var minStack = [];
30+
31+
function push(node)
32+
{
33+
dataStack.push(node);
34+
if(minStack.length === 0 || node < min()){
35+
minStack.push(node);
36+
}else{
37+
minStack.push(min());
38+
}
39+
}
40+
function pop()
41+
{
42+
minStack.pop();
43+
return dataStack.pop();
44+
}
45+
function top()
46+
{
47+
var length = dataStack.length;
48+
return length>0&&dataStack[length-1]
49+
}
50+
function min()
51+
{
52+
var length = minStack.length;
53+
return length>0&&minStack[length-1]
54+
}
55+
```
56+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
{
3+
"title": "栈的压入弹出序列",
4+
}
5+
---
6+
7+
8+
## 栈的压入、弹出序列
9+
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列`1,2,3,4,5`是某栈的压入顺序,序列`4,5,3,2,1`是该压栈序列对应的一个弹出序列,但`4,3,5,1,2`就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
10+
11+
## 思路
12+
13+
1.借助一个辅助栈来模拟压入、弹出的过程。
14+
15+
2.设置一个索引`idx`,记录`popV`(出栈序列)栈顶的位置
16+
17+
3.将`pushV`(压入顺序)中的数据依次入栈。
18+
19+
4.当辅助栈栈顶元素和压`popV`栈顶元素相同时,辅助栈出栈。每次出栈索引`idx`+1。
20+
21+
5.出栈有可能在任意一次入栈后进行,当辅助栈栈顶元素和压`popV`栈顶元素相同时,继续让`pushV`入辅助栈。
22+
23+
6.当所有数据入栈完成,如果出栈顺序正确,那么辅助栈应该为空。
24+
25+
26+
![](../../dist/img/栈的压入弹出序列.png)
27+
28+
## 代码
29+
30+
```js
31+
function IsPopOrder(pushV, popV) {
32+
if (!pushV || !popV || pushV.length == 0 || popV.length == 0) {
33+
return;
34+
}
35+
var stack = [];
36+
var idx = 0;
37+
for (var i = 0; i < pushV.length; i++) {
38+
stack.push(pushV[i]);
39+
while (stack.length && stack[stack.length - 1] == popV[idx]) {
40+
stack.pop();
41+
idx++;
42+
}
43+
}
44+
return stack.length == 0;
45+
}
46+
47+
```
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
{
3+
"title": "滑动窗口的最大值",
4+
}
5+
---
6+
7+
## 题目
8+
9+
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。
10+
返回滑动窗口最大值。
11+
```js
12+
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
13+
输出: [3,3,5,5,6,7]
14+
解释:
15+
滑动窗口的位置 最大值
16+
--------------- -----
17+
[1 3 -1] -3 5 3 6 7 3
18+
1 [3 -1 -3] 5 3 6 7 3
19+
1 3 [-1 -3 5] 3 6 7 5
20+
1 3 -1 [-3 5 3] 6 7 5
21+
1 3 -1 -3 [5 3 6] 7 6
22+
1 3 -1 -3 5 [3 6 7] 7
23+
```
24+
25+
## 思路
26+
27+
使用一个双端队列(队列两面都可进出),用于存储处于窗口中的值的下标,保证窗口头部元素永远是窗口最大值
28+
- 1.当前进入的元素下标 - 窗口头部元素的下标 >= k 头部元素移出队列
29+
- 2.如果当前数字大于队列尾,则删除队列尾,直到当前数字小于等于队列尾,或者队列空 (保证窗口中左侧的值均大于当前入队列的值,这样做可以保证当下次循环窗口头部的元素出队后,窗口头部元素仍然为最大值)
30+
- 3.队列元素入队
31+
- 4.第k次遍历后开始向结果中添加最大值
32+
33+
![](../../dist/img/滑动窗口的最大值.png)
34+
35+
时间复杂度:`O(n)`
36+
37+
空间复杂度:`O(n)`
38+
39+
- 使用优先队列也可以实现,时间复杂度为`O(nlogk)`
40+
41+
## 代码
42+
43+
```js
44+
var maxSlidingWindow = function (nums, k) {
45+
const window = [];
46+
const result = [];
47+
for (let i = 0; i < nums.length; i++) {
48+
if (i - window[0] > k - 1) {
49+
window.shift();
50+
}
51+
let j = window.length - 1;
52+
while (j >= 0 && nums[window[j]] <= nums[i]) {
53+
j--;
54+
window.pop();
55+
}
56+
window.push(i);
57+
if (i >= k - 1) {
58+
result.push(nums[window[0]]);
59+
}
60+
}
61+
return result;
62+
};
63+
```
64+
65+
## 考察点
66+
67+
- 队列
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
{
3+
"title": "用两个栈实现队列",
4+
}
5+
---
6+
7+
8+
## 题目
9+
10+
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
11+
12+
## 思路
13+
14+
**栈1:**
15+
16+
用于入队列存储
17+
18+
**栈2:**
19+
20+
出队列时将栈1的数据依次出栈,并入栈到栈2中
21+
22+
栈2出栈即栈1的底部数据即队列要出的数据。
23+
24+
**注意:**
25+
26+
栈2为空才能补充栈1的数据,否则会打乱当前的顺序。
27+
28+
![](../../dist/img/queue.png)
29+
30+
## 代码
31+
32+
33+
```js
34+
const stack1 = [];
35+
const stack2 = [];
36+
37+
function push(node)
38+
{
39+
stack1.push(node);
40+
}
41+
function pop()
42+
{
43+
if(stack2.length === 0){
44+
while(stack1.length>0){
45+
stack2.push(stack1.pop());
46+
}
47+
}
48+
return stack2.pop() || null;
49+
}
50+
```
51+
52+
53+
## 扩展:用两个队列实现一个栈
54+
55+
```js
56+
const queue1 = []
57+
const queue2 = []
58+
59+
function push(x) {
60+
if (queue1.length === 0) {
61+
queue1.push(x)
62+
63+
while (queue2.length) {
64+
queue1.push(queue2.shift())
65+
}
66+
} else if (queue2.length === 0) {
67+
queue2.push(x)
68+
69+
while (queue1.length) {
70+
queue2.push(queue1.shift())
71+
}
72+
}
73+
};
74+
75+
function pop() {
76+
if (queue1.length !== 0) {
77+
return queue1.shift()
78+
} else {
79+
return queue2.shift()
80+
}
81+
};
82+
```

0 commit comments

Comments
 (0)