题目
1381. 设计一个支持增量操作的栈
中等
请你设计一个支持下述操作的栈。
实现自定义栈类 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 。
示例:
输入:
["CustomStack","push","push","pop","push","push","push","increment","increment","pop","pop","pop","pop"]
[[3],[1],[2],[],[2],[3],[4],[5,100],[2,100],[],[],[],[]]
输出:
[null,null,null,2,null,null,null,null,null,103,202,201,-1]
解释:
CustomStack customStack = new CustomStack(3); // 栈是空的 []
customStack.push(1); // 栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.pop(); // 返回 2 --> 返回栈顶值 2,栈变为 [1]
customStack.push(2); // 栈变为 [1, 2]
customStack.push(3); // 栈变为 [1, 2, 3]
customStack.push(4); // 栈仍然是 [1, 2, 3],不能添加其他元素使栈大小变为 4
customStack.increment(5, 100); // 栈变为 [101, 102, 103]
customStack.increment(2, 100); // 栈变为 [201, 202, 103]
customStack.pop(); // 返回 103 --> 返回栈顶值 103,栈变为 [201, 202]
customStack.pop(); // 返回 202 --> 返回栈顶值 202,栈变为 [201]
customStack.pop(); // 返回 201 --> 返回栈顶值 201,栈变为 []
customStack.pop(); // 返回 -1 --> 栈为空,返回 -1
提示:
1 <= maxSize, x, k <= 10000 <= val <= 100- 每种方法
increment,push以及pop分别最多调用1000次
leetcode地址:leetcode-cn.com/problems/de…
解法1:数组实现栈
思路
最简单直接的方式就是用数组模拟栈,push 和 pop 时只需要操作数组原生的方法即可,increment 时为栈中的每个元素都加上增量的值
代码
rust 代码
struct CustomStack {
max_size: usize,
stack: Vec<i32>,
}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl CustomStack {
fn new(max_size: i32) -> Self {
Self {
max_size: max_size as usize,
stack: Vec::new(),
}
}
fn push(&mut self, x: i32) {
if self.stack.len() < self.max_size {
self.stack.push(x);
}
}
fn pop(&mut self) -> i32 {
self.stack.pop().unwrap_or(-1)
}
fn increment(&mut self, k: i32, val: i32) {
let k = std::cmp::min(k as usize, self.stack.len());
// 有增量时,取 stack 中索引小于 k 的每个元素加上增量的值再次存入 stack 中
for i in 0..k {
*(self.stack.get_mut(i).unwrap()) += val;
}
}
}
/**
* Your CustomStack object will be instantiated and called as such:
* let obj = CustomStack::new(maxSize);
* obj.push(x);
* let ret_2: i32 = obj.pop();
* obj.increment(k, val);
*/
js 代码
/**
* @param {number} maxSize
*/
var CustomStack = function(maxSize) {
this.maxSize = maxSize;
this.stack = []; // 数组前面的元素是栈底
};
/**
* @param {number} x
* @return {void}
*/
CustomStack.prototype.push = function(x) {
if (this.stack.length >= this.maxSize) {
return;
}
this.stack.push(x);
};
/**
* @return {number}
*/
CustomStack.prototype.pop = function() {
return this.stack.length ? this.stack.pop() : -1;
};
/**
* @param {number} k
* @param {number} val
* @return {void}
*/
CustomStack.prototype.increment = function(k, val) {
// 有增量时,取 stack 中索引小于 k 的每个元素加上增量的值再次存入 stack 中
let length = Math.min(k, this.stack.length);
for (let i = 0; i < length; i++) {
this.stack[i] += val;
}
};
/**
* Your CustomStack object will be instantiated and called as such:
* var obj = new CustomStack(maxSize)
* obj.push(x)
* var param_2 = obj.pop()
* obj.increment(k,val)
*/
复杂度
- 时间复杂度
push: O(1)、pop: O(1)、increment:O(N),N为min(k, 栈中元素的个数) - 空间复杂度
O(N),N为maxSize
解法2:哈希表 + 数组(空间换时间)
思路
解法 2 是 解法 1 的优化,在解法 1 的基础上把 increment 的时间复杂度从 O(N) 降为 O(1)。
核心: 是增量不实时计算,而是先保存增量,等到 pop 获取数据时,才实时把增量添加到 pop 出来的值
类似按需加载,而不是在
increment时为栈的每个元素都添加增量
空间换时间
- 使用一个
stack数组存储栈中的值 - 使用一个哈希表
hashMap保存下标和增量的关系,key是stack数组元素的下标,value是"当前下标的值"要增加的增量值,表示stack[key]元素本身以及下标小于key的元素在pop时要增加的增量 - 在
increment方法中只保存下标和增量关系到哈希表 - 在
pop时才去为"弹出的元素"加上增量,pop后的元素对应的下标top_index的增量要置为0
注意: pop 栈顶元素后,如果 pop 出的元素有增量(假设栈顶元素的下标为 top_index ),则哈希表中要为 top_index - 1 这个下标增加一样的增量。因为哈希表保存的增量并不是键 key 这个下标对应元素自己的增量 ,而是小于等于 key 的所有下标对应元素的增量,现在栈顶元素出栈了,下一个充当栈顶的元素只有加上出栈的栈顶元素的增量,这样每次出栈,才能把小于 key 的所有元素都增加一样的增量)
代码
rust 代码
use std::collections::HashSet;
use std::collections::HashMap;
struct CustomStack {
max_size: usize,
// 用数组保存栈的数据
stack: Vec<i32>,
// key是数组下标,value是增量
incr_hash_map: HashMap<usize, i32>,
}
/**
* `&self` means the method takes an immutable reference.
* If you need a mutable reference, change it to `&mut self` instead.
*/
impl CustomStack {
fn new(max_size: i32) -> Self {
Self {
max_size: max_size as usize,
stack: Vec::new(),
incr_hash_map: HashMap::new()
}
}
fn push(&mut self, x: i32) {
if self.stack.len() < self.max_size {
self.stack.push(x);
}
}
fn pop(&mut self) -> i32 {
// 栈为空
if self.stack.is_empty() {
return -1;
}
let top_index = self.stack.len() - 1;
// 栈顶元素的增量,取出来后要清空,然后 top_index - 1 的索引要保存增量,取出来后删掉增量
let inc_value = self.incr_hash_map.remove(&top_index).unwrap_or(0);
// 返回结果就是栈顶元素加上增量
let top_value = self.stack.pop().unwrap();
let result = top_value + inc_value;
// 为 top_index - 1 的索引保存增量,表示 top_index - 1 的元素出栈时也需要加上增量
// new_top_index 为 top_index - 1
if let Some(new_top_index) = self.stack.len().checked_sub(1) {
// 获取 new_top_index 键值对,key 不存在则设置值为0,然后加上增量
*self.incr_hash_map.entry(new_top_index).or_insert(0) += inc_value;
}
result
}
fn increment(&mut self, k: i32, val: i32) {
let index = k.min(self.stack.len() as i32) - 1;
if index < 0 {
return;
}
// key不存在则是指值为0,然后为key加上 val
*self.incr_hash_map.entry(index as usize).or_insert(0) += val;
}
}
/**
* Your CustomStack object will be instantiated and called as such:
* let obj = CustomStack::new(maxSize);
* obj.push(x);
* let ret_2: i32 = obj.pop();
* obj.increment(k, val);
*/
js 代码
/**
* @param {number} maxSize
*/
var CustomStack = function(maxSize) {
this.maxSize = maxSize;
// 用数组保存栈的数据
this.stack = [];
// key是数组下标,value是增量
this.hashMap = new Map();
};
/**
* @param {number} x
* @return {void}
*/
CustomStack.prototype.push = function(x) {
if (this.stack.length < this.maxSize) {
this.stack.push(x);
}
};
/**
* @return {number}
*/
CustomStack.prototype.pop = function() {
let topIndex = this.stack.length - 1;
if (topIndex < 0) {
return -1;
}
// 栈顶元素的增量,取出来后要清空,然后 topIndex - 1 的索引要保存增量
let incValue = this.hashMap.get(topIndex) || 0;
// 删掉增量
this.hashMap.delete(topIndex);
// 栈顶元素加上增量
let result = this.stack.pop() + incValue;
let newTopIndex = topIndex - 1;
// 注意:为 topIndex - 1 的索引保存增量,表示 topIndex - 1 出栈时也需要加上增量
this.hashMap.set(newTopIndex, (this.hashMap.get(newTopIndex) || 0) + incValue);
return result;
};
/**
* @param {number} k
* @param {number} val
* @return {void}
*/
CustomStack.prototype.increment = function(k, val) {
let index = Math.min(this.stack.length, k) - 1;
if (index < 0) {
return;
}
this.hashMap.set(index, (this.hashMap.get(index) || 0 ) + val);
};
/**
* Your CustomStack object will be instantiated and called as such:
* var obj = new CustomStack(maxSize)
* obj.push(x)
* var param_2 = obj.pop()
* obj.increment(k,val)
*/
复杂度
- 时间复杂度
push: O(1)、pop: O(1)、increment: O(1),N为min(K, 栈中元素的个数) - 空间复杂度
O(N),N为maxSize