11# 题目地址(1381. 设计一个支持增量操作的栈)
22
3- https://leetcode-cn.com/problems/design-a-stack-with-increment-operation/
3+ https://leetcode-cn.com/problems/plus-one
44
55## 题目描述
66
@@ -52,11 +52,11 @@ customStack.pop(); // 返回 -1 --> 栈为空,返回 -1
5252- 栈
5353- 前缀和
5454
55- ## increment 时间复杂度为 $$ O(k) $ $ 的方法
55+ ## increment 时间复杂度为 $O(k)$ 的方法
5656
5757### 思路
5858
59- 首先我们来看一种非常符合直觉的方法,然而这种方法并不好,increment 操作需要的时间复杂度为 $$ O(k) $ $ 。
59+ 首先我们来看一种非常符合直觉的方法,然而这种方法并不好,increment 操作需要的时间复杂度为 $O(k)$。
6060
6161` push ` 和 ` pop ` 就是普通的栈操作。 唯一要注意的是边界条件,这个已经在题目中指明了,具体来说就是:
6262
@@ -95,8 +95,8 @@ class CustomStack:
9595
9696** 复杂度分析**
9797
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)$
100100
101101## 前缀和
102102
@@ -112,7 +112,7 @@ class CustomStack:
112112- push 操作不变,和上面一样
113113- increment 的时候,我们将用到 incremental 信息。那么这个信息是什么,从哪来呢?我这里画了一个图
114114
115- ![ image ] ( https://user-images.githubusercontent.com/12479470/83656933-c096d300-a5f2-11ea-8f50-64ced5aa62f2.png )
115+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx11x0l0j30u014itck.jpg )
116116
117117如图黄色部分是我们需要执行增加操作,我这里画了一个挡板分割,实际上这个挡板不存在。那么如何记录黄色部分的信息呢?我举个例子来说
118118
@@ -121,15 +121,14 @@ class CustomStack:
121121- 调用了 increment(3, 2),就把 increment[ 3] 增加 2。
122122- 继续调用 increment(2, 5),就把 increment[ 2] 增加 5。
123123
124- ![ image ] ( https://user-images.githubusercontent.com/12479470/83640207-6855d600-a5de-11ea-809e-bba303927707.png )
124+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx1fk7vcj30nm0c8wfb.jpg )
125125
126126而当我们 pop 的时候:
127127
128128- 只需要将栈顶元素** 加上 increment[ cnt - 1] ** 即可, 其中 cnt 为栈当前的大小。
129129- 另外,我们需要将 increment[ cnt - 1] 更新到 increment[ cnt - 2] ,并将 increment[ cnt - 1] 重置为 0。
130130
131- ![ image] ( https://user-images.githubusercontent.com/12479470/83640238-7146a780-a5de-11ea-8b81-81439353068f.png )
132-
131+ ![ ] ( https://tva1.sinaimg.cn/large/0081Kckwly1glwx1ryzxpj31jq0hijte.jpg )
133132### 代码
134133
135134``` py
@@ -164,25 +163,21 @@ class CustomStack:
164163
165164** 复杂度分析**
166165
167- - 时间复杂度:全部都是 $$ O(1) $ $
168- - 空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为 $$ O(maxSize / N) $ $ ,其中 N 为操作数。
166+ - 时间复杂度:全部都是 $O(1)$
167+ - 空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为 $O(maxSize / N)$,其中 N 为操作数。
169168
170169## 优化的前缀和
171170
172171### 思路
173172
174- 上面的思路无论如何,我们都需要维护一个大小为 $$ O(maxSize) $$ 的数组 incremental 。而由于栈只能在栈顶进行操作,因此这实际上可以稍微优化一点,即维护一个大小为当前栈长度的 incrementals,而不是 $$ O(maxSize) $ $ 。
173+ 上面的思路无论如何,我们都需要维护一个大小为 $O(maxSize)$ 的数组 incremental 。而由于栈只能在栈顶进行操作,因此这实际上可以稍微优化一点,即维护一个大小为当前栈长度的 incrementals,而不是 $O(maxSize)$ 。
175174
176175每次栈 push 的时候,incrementals 也 push 一个 0。每次栈 pop 的时候, incrementals 也 pop,这样就可以了。
177176
178177> 这里的 incrementals 并不是一个栈,而是一个普通数组,因此可以随机访问。
179178
180179### 代码
181180
182- 代码支持: Python3, Go, PHP
183-
184- Python3 Code:
185-
186181``` py
187182class CustomStack :
188183
@@ -212,123 +207,10 @@ class CustomStack:
212207 self .incrementals[min (self .cnt, k) - 1 ] += val
213208```
214209
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-
328210** 复杂度分析**
329211
330- - 时间复杂度:全部都是 $$ O(1) $ $
331- - 空间复杂度:我们维护了一个大小为 cnt 的数组,因此平均到每次的空间复杂度为 $$ O(cnt / N) $ $ ,其中 N 为操作数,cnt 为操作过程中的栈的最大长度(小于等于 maxSize)。
212+ - 时间复杂度:全部都是 $O(1)$
213+ - 空间复杂度:我们维护了一个大小为 cnt 的数组,因此平均到每次的空间复杂度为 $O(cnt / N)$,其中 N 为操作数,cnt 为操作过程中的栈的最大长度(小于等于 maxSize)。
332214
333215可以看出优化的解法在 maxSize 非常大的时候是很有意义的。
334216
0 commit comments