1
1
# 题目地址(1381. 设计一个支持增量操作的栈)
2
2
3
- https://leetcode-cn.com/problems/design-a-stack-with-increment-operation/
3
+ https://leetcode-cn.com/problems/plus-one
4
4
5
5
## 题目描述
6
6
@@ -52,11 +52,11 @@ customStack.pop(); // 返回 -1 --> 栈为空,返回 -1
52
52
- 栈
53
53
- 前缀和
54
54
55
- ## increment 时间复杂度为 $$ O(k) $ $ 的方法
55
+ ## increment 时间复杂度为 $O(k)$ 的方法
56
56
57
57
### 思路
58
58
59
- 首先我们来看一种非常符合直觉的方法,然而这种方法并不好,increment 操作需要的时间复杂度为 $$ O(k) $ $ 。
59
+ 首先我们来看一种非常符合直觉的方法,然而这种方法并不好,increment 操作需要的时间复杂度为 $O(k)$。
60
60
61
61
` push ` 和 ` pop ` 就是普通的栈操作。 唯一要注意的是边界条件,这个已经在题目中指明了,具体来说就是:
62
62
@@ -95,8 +95,8 @@ class CustomStack:
95
95
96
96
** 复杂度分析**
97
97
98
- - 时间复杂度:push 和 pop 操作的时间复杂度为 $$ O(1) $$ (讲义有提到),而 increment 操作的时间复杂度为 $$ O(min(k, cnt)) $ $
99
- - 空间复杂度:$$ O(1) $ $
98
+ - 时间复杂度:push 和 pop 操作的时间复杂度为 $O(1)$(讲义有提到),而 increment 操作的时间复杂度为 $O(min(k, cnt))$
99
+ - 空间复杂度:$O(1)$
100
100
101
101
## 前缀和
102
102
@@ -112,7 +112,7 @@ class CustomStack:
112
112
- push 操作不变,和上面一样
113
113
- increment 的时候,我们将用到 incremental 信息。那么这个信息是什么,从哪来呢?我这里画了一个图
114
114
115
- ![ image ] ( https://user-images.githubusercontent.com/12479470/83656933-c096d300-a5f2-11ea-8f50-64ced5aa62f2.png )
115
+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx11x0l0j30u014itck.jpg )
116
116
117
117
如图黄色部分是我们需要执行增加操作,我这里画了一个挡板分割,实际上这个挡板不存在。那么如何记录黄色部分的信息呢?我举个例子来说
118
118
@@ -121,15 +121,14 @@ class CustomStack:
121
121
- 调用了 increment(3, 2),就把 increment[ 3] 增加 2。
122
122
- 继续调用 increment(2, 5),就把 increment[ 2] 增加 5。
123
123
124
- ![ image ] ( https://user-images.githubusercontent.com/12479470/83640207-6855d600-a5de-11ea-809e-bba303927707.png )
124
+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx1fk7vcj30nm0c8wfb.jpg )
125
125
126
126
而当我们 pop 的时候:
127
127
128
128
- 只需要将栈顶元素** 加上 increment[ cnt - 1] ** 即可, 其中 cnt 为栈当前的大小。
129
129
- 另外,我们需要将 increment[ cnt - 1] 更新到 increment[ cnt - 2] ,并将 increment[ cnt - 1] 重置为 0。
130
130
131
- ![ image] ( https://user-images.githubusercontent.com/12479470/83640238-7146a780-a5de-11ea-8b81-81439353068f.png )
132
-
131
+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx1ryzxpj31jq0hijte.jpg )
133
132
### 代码
134
133
135
134
``` py
@@ -164,25 +163,21 @@ class CustomStack:
164
163
165
164
** 复杂度分析**
166
165
167
- - 时间复杂度:全部都是 $$ O(1) $ $
168
- - 空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为 $$ O(maxSize / N) $ $ ,其中 N 为操作数。
166
+ - 时间复杂度:全部都是 $O(1)$
167
+ - 空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为 $O(maxSize / N)$,其中 N 为操作数。
169
168
170
169
## 优化的前缀和
171
170
172
171
### 思路
173
172
174
- 上面的思路无论如何,我们都需要维护一个大小为 $$ O(maxSize) $$ 的数组 incremental 。而由于栈只能在栈顶进行操作,因此这实际上可以稍微优化一点,即维护一个大小为当前栈长度的 incrementals,而不是 $$ O(maxSize) $ $ 。
173
+ 上面的思路无论如何,我们都需要维护一个大小为 $O(maxSize)$ 的数组 incremental 。而由于栈只能在栈顶进行操作,因此这实际上可以稍微优化一点,即维护一个大小为当前栈长度的 incrementals,而不是 $O(maxSize)$ 。
175
174
176
175
每次栈 push 的时候,incrementals 也 push 一个 0。每次栈 pop 的时候, incrementals 也 pop,这样就可以了。
177
176
178
177
> 这里的 incrementals 并不是一个栈,而是一个普通数组,因此可以随机访问。
179
178
180
179
### 代码
181
180
182
- 代码支持: Python3, Go, PHP
183
-
184
- Python3 Code:
185
-
186
181
``` py
187
182
class CustomStack :
188
183
@@ -212,123 +207,10 @@ class CustomStack:
212
207
self .incrementals[min (self .cnt, k) - 1 ] += val
213
208
```
214
209
215
- Go Code:
216
-
217
- ``` go
218
- type CustomStack struct {
219
- Stack []int
220
- PreSum []int // 前缀和
221
- Cnt int
222
- Size int
223
- Top int
224
- }
225
-
226
- func Constructor (maxSize int ) CustomStack {
227
- // 因为 go 语言 slice 底层实现机制, 如果不设置 cap, 会导致频繁扩容, 可能增大时间/内存消耗
228
- // 提交测试后的最佳组合: Stack 使用默认值, PreSum 预设为 MaxSize
229
- return CustomStack{Size: maxSize, PreSum: make ([]int , maxSize, maxSize)}
230
- }
231
-
232
- func (this *CustomStack ) Push (x int ) {
233
- if this.Cnt < this.Size {
234
- this.Stack = append (this.Stack , x)
235
- this.Cnt ++
236
- }
237
- }
238
-
239
- func (this *CustomStack ) Pop () int {
240
- if this.Cnt == 0 {
241
- return -1
242
- }
243
- n := len (this.Stack )
244
- var a int
245
- a, this.Stack = this.Stack [n-1 ], this.Stack [:n-1 ]
246
- this.Top = a + this.PreSum [n-1 ]
247
- if this.Cnt >=2 { // 重置
248
- this.PreSum [n-2 ] += this.PreSum [n-1 ]
249
- }
250
- this.PreSum [n-1 ] = 0
251
- this.Cnt --
252
- return this.Top
253
- }
254
-
255
- func (this *CustomStack ) Increment (k int , val int ) {
256
- if this.Cnt == 0 {
257
- return
258
- }
259
- n := min (k, this.Cnt )
260
- this.PreSum [n-1 ] += val
261
- }
262
-
263
- func min (a , b int ) int {
264
- if a < b {
265
- return a
266
- }
267
- return b
268
- }
269
- ```
270
-
271
- PHP Code:
272
-
273
- ``` php
274
- class customStack
275
- {
276
- public $stack = [];
277
- public $preSum = [];
278
- public $size;
279
- public $cnt = 0;
280
- public $top = -1; // debug
281
-
282
- /**
283
- * @param Integer $maxSize
284
- */
285
- function __construct($maxSize)
286
- {
287
- $this->size = $maxSize;
288
- }
289
-
290
- /**
291
- * @param Integer $x
292
- * @return NULL
293
- */
294
- function push($x)
295
- {
296
- if ($this->cnt < $this->size) {
297
- array_push($this->stack, $x);
298
- array_push($this->preSum, 0);
299
- $this->cnt++;
300
- }
301
- }
302
-
303
- /**
304
- * @return Integer
305
- */
306
- function pop()
307
- {
308
- if (!$this->cnt) return -1;
309
- if ($this->cnt >= 2) $this->preSum[$this->cnt - 2] += $this->preSum[$this->cnt - 1];
310
- $this->cnt--;
311
- $this->top = array_pop($this->stack) + array_pop($this->preSum);
312
- return $this->top;
313
- }
314
-
315
- /**
316
- * @param Integer $k
317
- * @param Integer $val
318
- * @return NULL
319
- */
320
- function increment($k, $val)
321
- {
322
- $k = min($k, $this->cnt);
323
- if ($k) $this->preSum[$k - 1] += $val;
324
- }
325
- }
326
- ```
327
-
328
210
** 复杂度分析**
329
211
330
- - 时间复杂度:全部都是 $$ O(1) $ $
331
- - 空间复杂度:我们维护了一个大小为 cnt 的数组,因此平均到每次的空间复杂度为 $$ O(cnt / N) $ $ ,其中 N 为操作数,cnt 为操作过程中的栈的最大长度(小于等于 maxSize)。
212
+ - 时间复杂度:全部都是 $O(1)$
213
+ - 空间复杂度:我们维护了一个大小为 cnt 的数组,因此平均到每次的空间复杂度为 $O(cnt / N)$,其中 N 为操作数,cnt 为操作过程中的栈的最大长度(小于等于 maxSize)。
332
214
333
215
可以看出优化的解法在 maxSize 非常大的时候是很有意义的。
334
216
0 commit comments