哈希算法题目:设计一个支持增量操作的栈
字数 2208 2025-12-02 06:59:50

哈希算法题目:设计一个支持增量操作的栈

题目描述
设计一个栈数据结构,支持以下操作:

  1. push(val):将元素 val 压入栈顶。
  2. pop():弹出栈顶元素。
  3. increment(k, val):将栈底的前 k 个元素(即最早入栈的 k 个元素)每个增加 val。如果栈中元素少于 k 个,则将所有元素增加 val

要求所有操作的时间复杂度为 O(1)


解题过程循序渐进讲解

步骤1:分析问题难点
传统栈的 pushpop 操作本身是 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:完整方案
最终方案(使用双栈):

  1. stack:存储实际元素值。
  2. 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:举例验证
示例操作序列:

  1. push(1), push(2), push(3) → stack = [1,2,3], inc = [0,0,0]
  2. increment(2, 100) → k=2, idx=min(2,3)-1=1 → inc[1] += 100 → inc = [0,100,0]
  3. 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。
  4. pop()
    • lastInc = inc.pop()=100 → stack.pop()=2 → 返回 2+100=102
    • 此时 inc 变为 [0],stack 变为 [1]
    • 将 lastInc=100 加到 inc[0] 上 → inc[0]=100
  5. pop() → lastInc=100 → 返回 1+100=101

结果:弹出顺序为 3, 102, 101,符合预期(前2个元素增加了100)。

步骤7:复杂度分析

  • pushpopincrement 均只涉及常数次栈操作和加法,时间复杂度 O(1)
  • 空间复杂度 O(n)n 为栈最大深度。

关键点
通过“增量记录”和“弹出时传递增量”的策略,将本应遍历修改的 increment 操作均摊到每个 pop 上,实现了所有操作的 O(1) 时间复杂度。

哈希算法题目:设计一个支持增量操作的栈 题目描述 设计一个栈数据结构,支持以下操作: 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) 时间复杂度。