ST表(Sparse Table,稀疏表)是一种用于在静态数组(内容不变)上快速查询区间信息的数据结构。它特别擅长处理那些具有结合律并且结果可以重复计算而不影响最终值的操作(幂等性,如min/max)。
主要适用场景:
示例数组: (这个数组将在下面被用来演示)
核心特点与对比:
数据结构 | 预处理时间 | 查询时间 | 更新时间 | 空间复杂度 | 是否支持动态 |
---|---|---|---|---|---|
ST表 | O(n log n) | O(1)* | 不支持 | O(n log n) | 否 (静态) |
线段树 | O(n) | O(log n) | O(log n) | O(n) | 是 (动态) |
树状数组 | O(n log n) / O(n) | O(log n) | O(log n) | O(n) | 是 (动态) |
* O(1) 查询适用于幂等操作(如 min, max, gcd, and, or)。对于非幂等操作(如区间和),ST 表查询需要 O(log n) 或无法直接应用。
ST表的核心思想是动态规划:预先计算并存储所有长度为 2 的幂次方的区间的结果。查询时,任何区间都可以被表示为(最多)两个这样的预计算区间的组合。
定义:我们用 st[k][i]
表示原数组中,从索引 i
开始,长度为 2k
的区间 [i, i + 2k - 1]
的计算结果(例如,最小值)。
st[k][i] = min(st[k-1][i], st[k-1][i + 2k-1])
st[0][i]
存储的是长度为 20 = 1 的区间的值,即原数组元素 a[i]
。[i, i + 2k - 1]
可以被完美地拆分成两个相邻且等长的子区间:
[i, i + 2k-1 - 1]
(从 i 开始,长度 2k-1),其结果为 st[k-1][i]
。[i + 2k-1, i + 2k - 1]
(从 i + 2k-1 开始,长度 2k-1),其结果为 st[k-1][i + 2k-1]
。st[k][i]
的结果。
请输入一个小的整数数组(用空格分隔,例如:9 3 7 1 8 12 6 5):
ST表的最大优势在于能够以 O(1) 的时间复杂度查询静态区间信息(对于幂等操作)。这是通过巧妙地选择两个可能重叠的、长度为 2 的幂的预计算区间来实现的。
len = R - L + 1
k
,使得 2k
是不大于 len
的最大 2 的幂。即 k = ⌊log2(len)⌋
。 这保证了我们选取的子区间长度不会超过原区间长度。2k
的预计算区间,它们的并集完全覆盖查询区间 [L, R]
:
L
开始,长度为 2k
,即 [L, L + 2k - 1]
。其结果为 st[k][L]
。R
,长度为 2k
,即 [R - 2k + 1, R]
。其结果为 st[k][R - 2k + 1]
。注意:这两个区间可能会有重叠部分。对于 min, max, gcd, and, or 等幂等操作,重叠部分计算两次不影响最终结果。
result = min(st[k][L], st[k][R - 2k + 1])
使用上一步构建的ST表 (如果尚未构建,请先返回上一页构建)。请输入查询区间的左右端点 (0-based index):
ST表虽然查询效率极高,但它的应用场景有明确的限制。
区间最小值 (Min)
区间最大值 (Max)
区间最大公约数 (GCD)
区间按位与 (AND)
区间按位或 (OR)
其他结合律 & 幂等操作
区间和 (Sum)
区间异或 (XOR)
需要修改数组元素
动态增删元素
非幂等结合律操作查询 (需 O(log n))
当需要处理动态修改或不满足幂等性的操作时,应考虑使用线段树或树状数组等其他数据结构。
给定一个长度为 n 的整数数组 a。你需要实现一个数据结构,支持快速查询任意给定区间 [L, R] (0 ≤ L ≤ R < n) 内的最小值。
要求:
给定一个长度为 n 的正整数数组 a。你需要实现一个数据结构,支持快速查询任意给定区间 [L, R] 内所有元素的最大公约数 (GCD)。
提示:最大公约数运算 gcd(a, b) 满足结合律 (gcd(a, gcd(b, c)) = gcd(gcd(a, b), c)
) 和幂等性 (gcd(a, a) = a
)。因此,ST 表非常适合解决此问题。
要求:
O(n log n)
的存储空间。对于 n=105,log2n ≈ 17,所以大约需要 17 * 105
个整数的存储空间。vector> st;
推荐使用,更灵活。需要在构建时 resize
。例如: st.resize(K + 1, vector(n));
其中 K = ⌊log2n⌋。int st[MAX_K][MAX_N];
需要预估 K 和 N 的最大值 (例如 MAX_K = 20
, MAX_N = 100005
)。注意可能造成内存浪费或不足。0 ≤ L ≤ R < n
)。L == R
)。long long
类型防止整数溢出。k = ⌊log2(len)⌋
。// 预处理 O(N)
int log_table[MAX_N + 1];
void precompute_log(int n) {
log_table[1] = 0;
for (int i = 2; i <= n; i++) {
log_table[i] = log_table[i / 2] + 1;
}
}
// 查询时 O(1): k = log_table[R - L + 1];
// 查询时 O(1),但可能有微小常数开销
int k = 31 - __builtin_clz(R - L + 1); // For 32-bit int
// or __builtin_log2(R - L + 1) in C++20 <cmath>
// or log2(R - L + 1) from <cmath> (returns double, need floor)
__builtin_clz
计算前导零的数量,效率很高。
// 查询时 O(log len)
int k = 0;
while ((1 << (k + 1)) <= (R - L + 1)) {
k++;
}
i
的上界应为 n - (1 << k)
或类似形式,确保 i + (1 << (k - 1))
不越界。len = R - L + 1
。R - (1 << k) + 1
。ST 表是解决特定 RMQ (区间最值查询) 问题的利器,它的思想也可以应用或与其他算法结合:
求解树上两个节点的最近公共祖先 (LCA) 是一个经典问题。一种高效的方法是:
ST 表可以扩展到二维甚至更高维度,用于查询矩形区域内的最值等信息。
实现相对复杂,但在特定静态二维查询场景下有效。
选择哪种数据结构取决于问题的具体需求:是否需要修改?查询的操作类型?时间/空间限制?
感谢学习!希望这个教程对你理解和使用 ST 表有所帮助。