哈希算法题目:设计一个支持增量操作的栈
字数 2208 2025-12-02 06:59:50
哈希算法题目:设计一个支持增量操作的栈
题目描述
设计一个栈数据结构,支持以下操作:
push(val):将元素val压入栈顶。pop():弹出栈顶元素。increment(k, val):将栈底的前k个元素(即最早入栈的k个元素)每个增加val。如果栈中元素少于k个,则将所有元素增加val。
要求所有操作的时间复杂度为 O(1)。
解题过程循序渐进讲解
步骤1:分析问题难点
传统栈的 push 和 pop 操作本身是 O(1) 的,但 increment 操作如果直接修改前 k 个元素,需要遍历栈底部分,时间复杂度为 O(k),不符合题目要求。因此,需要设计一种机制,将增量操作“延迟”到实际访问元素时再应用。
步骤2:延迟增量策略
核心思路是维护一个独立的“增量记录表”,记录在哪些位置需要增加多少值。具体方案:
- 使用两个栈:
stack:正常存储元素值。inc:记录增量信息,inc[i]表示从栈底到位置i的累积增量(具体细节需调整)。
- 但直接记录每个位置的增量仍会导致
increment操作时修改k个位置,复杂度为O(k)。需要优化。
步骤3:优化增量记录
改为只记录增量作用的边界:
- 设
inc数组长度为n(栈最大容量),但初始为空。 - 当执行
increment(k, val)时,只需将增量val记录在inc[k-1]位置(表示前k个元素的增量)。若inc[k-1]已有值,则累加。 - 但这样在
pop时无法知道当前元素是否被增量影响,因为增量是作用在“前k个”而非“某个位置”。
步骤4:改进增量传递机制
更成熟的方案(类似“前缀和差分”思想):
- 维护一个
inc数组(长度与栈大小一致),inc[i]表示从位置i到栈底的所有元素需要增加的值的差分(即inc[i]仅影响位置i及之后的元素)。 - 具体实现:
push(val):向stack压入val,同时向inc压入0(新元素暂无增量)。increment(k, val):将val加到inc[min(k, stack.size()) - 1]上。注意这里inc的下标是从栈底开始计数的索引。
- 但此方案在
pop时需将当前增量传递给前一个元素,否则会丢失增量信息。
步骤5:完整方案
最终方案(使用双栈):
stack:存储实际元素值。inc:与stack等长,inc[i]表示从栈底到位置 i 的增量总和(前缀和形式)。但为了O(1)操作,我们只在increment时修改inc的某个位置,并在pop时传递增量。
具体操作:
push(val):stack.push(val)inc.push(0)
pop():- 如果栈非空,记
lastInc = inc.pop()(当前栈顶元素的增量值)。 - 若
inc非空,将lastInc加到新的栈顶对应的inc值上(增量传递)。 - 返回
stack.pop() + lastInc(实际值加上增量)。
- 如果栈非空,记
increment(k, val):- 如果栈非空,令
idx = min(k, stack.size()) - 1(栈底索引为0)。 - 将
val加到inc[idx]上。
- 如果栈非空,令
步骤6:举例验证
示例操作序列:
push(1),push(2),push(3)→ stack = [1,2,3], inc = [0,0,0]increment(2, 100)→ k=2, idx=min(2,3)-1=1 → inc[1] += 100 → inc = [0,100,0]pop()→- lastInc = inc.pop()=0 → stack.pop()=3 → 返回 3+0=3
- 此时 inc 变为 [0,100],stack 变为 [1,2]
- 注意 inc[1] 的增量需传递?不,因为 pop 的是栈顶(索引2),inc[1] 的增量是针对前2个元素的,不影响栈顶3。
pop()→- lastInc = inc.pop()=100 → stack.pop()=2 → 返回 2+100=102
- 此时 inc 变为 [0],stack 变为 [1]
- 将 lastInc=100 加到 inc[0] 上 → inc[0]=100
pop()→ lastInc=100 → 返回 1+100=101
结果:弹出顺序为 3, 102, 101,符合预期(前2个元素增加了100)。
步骤7:复杂度分析
push、pop、increment均只涉及常数次栈操作和加法,时间复杂度O(1)。- 空间复杂度
O(n),n为栈最大深度。
关键点
通过“增量记录”和“弹出时传递增量”的策略,将本应遍历修改的 increment 操作均摊到每个 pop 上,实现了所有操作的 O(1) 时间复杂度。