1381. 设计一个支持增量操作的栈
https://leetcode.cn/problems/design-a-stack-with-increment-operation/
题目描述
前置知识
栈
前缀和
increment 时间复杂度为 $O(k)$ 的方法
思路
首先我们来看一种非常符合直觉的方法,然而这种方法并不好,increment 操作需要的时间复杂度为 $O(k)$。
push
和 pop
就是普通的栈操作。 唯一要注意的是边界条件,这个已经在题目中指明了,具体来说就是:
push 的时候要判断是否满了
pop 的时候要判断是否空了
而做到上面两点,只需要一个 cnt 变量记录栈的当前长度,一个 size 变量记录最大容量,并在 pop 和 push 的时候更新 cnt 即可。
代码
复杂度分析
时间复杂度:push 和 pop 操作的时间复杂度为 $O(1)$(讲义有提到),而 increment 操作的时间复杂度为 $O(min(k, cnt))$
空间复杂度:$O(1)$
前缀和
前缀和在讲义里面提到过,大家也可是看下我的文章 一次搞定前缀和
思路
和上面的思路类似,不过我们采用空间换时间的方式。采用一个额外的数组 incrementals 来记录每次 incremental 操作。
具体算法如下:
初始化一个大小为 maxSize 的数组 incrementals, 并全部填充 0
push 操作不变,和上面一样
increment 的时候,我们将用到 incremental 信息。那么这个信息是什么,从哪来呢?我这里画了一个图
如图黄色部分是我们需要执行增加操作,我这里画了一个挡板分割,实际上这个挡板不存在。那么如何记录黄色部分的信息呢?我举个例子来说
比如:
调用了 increment(3, 2),就把 increment[3] 增加 2。
继续调用 increment(2, 5),就把 increment[2] 增加 5。
而当我们 pop 的时候:
只需要将栈顶元素加上 increment[cnt - 1] 即可, 其中 cnt 为栈当前的大小。
另外,我们需要将 increment[cnt - 1] 更新到 increment[cnt - 2],并将 increment[cnt - 1] 重置为 0。
代码
复杂度分析
时间复杂度:全部都是 $O(1)$
空间复杂度:我们维护了一个大小为 maxSize 的数组,因此平均到每次的空间复杂度为 $O(maxSize / N)$,其中 N 为操作数。
优化的前缀和
思路
上面的思路无论如何,我们都需要维护一个大小为 $O(maxSize)$ 的数组 incremental 。而由于栈只能在栈顶进行操作,因此这实际上可以稍微优化一点,即维护一个大小为当前栈长度的 incrementals,而不是 $O(maxSize)$ 。
每次栈 push 的时候,incrementals 也 push 一个 0。每次栈 pop 的时候, incrementals 也 pop,这样就可以了。
这里的 incrementals 并不是一个栈,而是一个普通数组,因此可以随机访问。
代码
复杂度分析
时间复杂度:全部都是 $O(1)$
空间复杂度:我们维护了一个大小为 cnt 的数组,因此平均到每次的空间复杂度为 $O(cnt / N)$,其中 N 为操作数,cnt 为操作过程中的栈的最大长度(小于等于 maxSize)。
可以看出优化的解法在 maxSize 非常大的时候是很有意义的。
相关题目
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。
大家也可以关注我的公众号《力扣加加》获取更多更新鲜的 LeetCode 题解
最后更新于