稀疏表(Sparse Table)

一句话说明

稀疏表提前预处理所有长度为 2^k 的区间答案,所以它适合“静态数组 + 大量区间最值查询”。

先判断它能不能用

稀疏表不是通用区间结构,它只在下面这种场景很强:

  • 数组建好之后不再修改
  • 查询很多
  • 维护的信息满足幂等性,比如 min、max、gcd

典型反例:

  • 区间和不适合直接用它做 O(1) 查询
  • 有修改操作时应该转向 线段树 或 树状数组

核心直觉

如果我们已经知道所有长度为 1、2、4、8 … 的区间答案,
那任意区间最值就能很快拼出来。

定义:

st[k][i] = 从 i 开始,长度为 2^k 的区间答案

例如当我们维护区间最小值时:

st[0][i] = nums[i]
st[1][i] = min(nums[i], nums[i+1])
st[2][i] = min(nums[i..i+3])

转移也很直接:

st[k][i] = min(st[k-1][i], st[k-1][i + 2^(k-1)])

也就是:

  • 长度 2^k 的区间
  • 拆成两个长度 2^(k-1) 的半区间

为什么查询能做到 O(1)

查询 [left, right] 时,设区间长度为 length,取:

k = floor(log2(length))

然后用两个长度都是 2^k 的区间去覆盖它:

[left, left+2^k-1]
[right-2^k+1, right]

这两个区间允许重叠。
之所以还能用,是因为对 min/max/gcd 这类运算来说,重复算一次没关系。

这就是稀疏表查询快的根本原因。

Go 代码:区间最小值模板

type SparseTable struct {
    log []int
    st  [][]int
}
 
func NewSparseTable(nums []int) *SparseTable {
    n := len(nums)
    if n == 0 {
        return &SparseTable{}
    }
 
    log := make([]int, n+1)
    for i := 2; i <= n; i++ {
        log[i] = log[i/2] + 1
    }
 
    levels := log[n] + 1
    st := make([][]int, levels)
    st[0] = append([]int(nil), nums...)
 
    for k := 1; k < levels; k++ {
        size := n - (1 << k) + 1
        st[k] = make([]int, size)
        half := 1 << (k - 1)
        for i := 0; i < size; i++ {
            st[k][i] = min(st[k-1][i], st[k-1][i+half])
        }
    }
 
    return &SparseTable{
        log: log,
        st:  st,
    }
}
 
func (sp *SparseTable) QueryMin(left, right int) int {
    length := right - left + 1
    k := sp.log[length]
    return min(sp.st[k][left], sp.st[k][right-(1<<k)+1])
}
 
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

这个模板里真正关键的不是代码,而是状态含义

写 ST 表最容易晕的地方是把下标含义搞混。
你只要一直咬死这个定义:

st[k][i] = 从 i 开始,长度为 2^k 的区间答案

后面的建表和查询都会很自然。

如果题目求最大值怎么办

只改合并函数即可。

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

然后把建表和查询里的 min 换成 max。

稀疏表和线段树怎么选

选稀疏表

  • 没有更新
  • 查询非常多
  • 只做最值 / gcd / 幂等查询
  • 想把单次查询压到 O(1)

选线段树

  • 题目有修改
  • 查询信息更复杂
  • 需要在线处理

也就是说,稀疏表是“静态查询极致优化版”,不是万能结构。

复杂度

阶段时间复杂度空间复杂度
预处理O(n log n)O(n log n)
单次查询O(1)O(1)

易错点

ST 表最常见的错误

  • length 一定是 right-left+1。
  • 第二段区间起点是 right-(1<<k)+1,这里特别容易少写 +1。
  • st[k] 的有效长度不是 n,而是 n-(1<<k)+1。
  • 求和不是幂等操作,不能直接套这个 O(1) 查询模板。

经典题目

  • RMQ(Range Minimum Query)模板题
  • 静态区间最大值查询
  • 静态区间 gcd 查询
  • 倍增和 LCA 相关题目的预处理理解

相关主题


返回:数据结构 | 算法学习导航