点击上方蓝字设为星标
下面开始今天的学习~
题目描述
请你设计一个支持下述操作的栈。
实现自定义栈类 CustomStack
:
CustomStack(int maxSize)
:用maxSize
初始化对象,maxSize
是栈中最多能容纳的元素数量,栈在增长到maxSize
之后则不支持push
操作。void push(int x)
:如果栈还未增长到maxSize
,就将 x 添加到栈顶。int pop()
:返回栈顶的值,或栈为空时返回 -1 。void inc(int k, int val)
:栈底的 k 个元素的值都增加val
。如果栈中元素总数小于 k ,则栈中的所有元素都增加 val 。
题目解析
题目让你设计一个类似栈的数据结构,这个栈除了基本的 push
和 pop
功能之外,还需要额外支持一个 “从栈底开始的 k 个元素自增 value” 的功能。
想要 AC 这道题目不难,你可以用一个数组来实现栈,对于 “元素自增” 功能,我们就遍历数组的前 k 个值,让其自增即可。
但是仔细想想,这样做下来,“元素自增” 功能的时间复杂度就会是 O(min(k, n))
其中 n 是栈中当前元素的总数。
我们知道,栈其实是属线性结构,它的两个原始操作都是 O(1)
的时间复杂度,如果我们能够让这个额外功能也具有 O(1)
的时间复杂度那就再好不过了。
那该如何去思考这个问题呢?
如果不能一次性自增对应的元素,那我们唯一能做的就是保存这个 “元素自增” 操作对应的数据,以备后用。
这里哪些数据是我们必须看重的?
其实就是两个,k 和 value,我们需要知道当前自增操作覆盖栈中的哪些元素。
这里有一点特别重要,就是我们只需要记录最高位置,因为最高位置往下其实都生效。
到了这个位置,以后每次 stack 做 pop
操作都需要加上 value。当然,如果在这个时候,往 stack 中进行 push
操作,之前的 value 是不会对 push
进来的新值生效的。
于是基于这两点,我们可以创建一个数组,这个数组专门记录奏效的最高位置,然后这个位置过了之后,就将对应的值清零,并将值移加到下一个位置。
在这之中,有一个重点就是,生效的元素在任何时候都不能大于栈中现存的元素。
这样下来,我们可以把时间复杂度降到 O(1)
,符合我们的预期。
参考代码
class CustomStack {
private Stack<Integer> stack;
private int[] inc;
private int maxSize;
public CustomStack(int maxSize) {
this.maxSize = maxSize;
this.inc = new int[maxSize];
this.stack = new Stack<>();
}
public void push(int x) {
if (stack.size() == maxSize) {
return;
}
stack.add(x);
}
public int pop() {
int index = stack.size() - 1;
if (index < 0) {
return -1;
}
int result = inc[index] + stack.pop();
// 对高位生效的 value,也同样对低位生效
// 将高位的值移加到低位
if (index > 0) {
inc[index - 1] += inc[index];
}
// 高位的值清零
inc[index] = 0;
return result;
}
public void increment(int k, int val) {
// 栈为空,自增操作不会对任何元素生效,退出
if (stack.isEmpty()) {
return;
}
// 必须保证生效的值不超过当前存在的元素的个数范围
int index = Math.min(k, stack.size()) - 1;
// 记录最高值
inc[index] += val;
}
}
END
点“在看”你懂得