ST表基础解释

本文介绍了如何使用ST表(Segment Tree)解决在线性表中查找区间最大值和最小值(RMQ)问题,通过预处理将查询时间复杂度降至O(nlogn) + O(1),在大规模数据和频繁查询场景中表现出色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ST表简介——处理RMQ问题的利器

前言:什么是RMQ,就是在线性表内处理出一段区间的最大值和最小值问题。如果你利用普通的for查找的话,时间复杂度为O(l-r+1),但是如果你用ST表预处理一下,时间复杂度为O(nlogn)加上每次查询的复杂度为O(1)是不是快捷很多。

比如一个线性表长度为1e5,查询次数为1e6,那么毫无疑问,朴素算法直接爆炸,
但是ST表就不会,而且到了后面的查询会十分迅速。

开始介绍算法原理,如果我们要存一个长度为n的线性表,并构建ST表的的话,我们要建一个
st[n][   n l o g 2 \ n{log2}  nlog2] 这样长度的二维表。

设st_min[n][   n l o g 2 \ n{log2}  nlog2],则st_min[i][j]所表示的内容则为i~i+2^j-1范围内的min值。那么对于st[i][0]时,很显然就是原数组[i]本身。那么我们怎么向j>0的范围扩展呢。

先放代码:

int t=log(n)/log(2)+1;
for(int j=1;j<t;j++)
    for(int i=1;i<=n-(1<<j)+1;i++)
      num1[i][j]=max(num1[i][j-1],num1[i+(1<<(j-1))][j-1]);

我们来看一下,为了确定覆盖了整个原数组的范围,要加上个一。
假设初始数据为这样:
1
我们来走一下,第一步:i=j=1,num1[1][1]=min(num1[1][0],num[2][0]),是不是符合上面ST表的定义
那么就变成这样

2

第二步:i=2,j=1,num1[2][1]=min(num1[2][0],num1[3][0]);则第三步为:
1
这个时候大家就知道for循环的大概一个趋势是一个下三角的趋势。(脑补一下)

那么预处理就完成了。接下来怎么查询的问题,已知查询区间,L和R,那么我们可以定义一个k,吧查询长度转换成上文的j,即:

k=log(r-l+1)/log(2);

那么我们怎么处理呢,我们知道,一个数它一般是不能一2^K表示出来的,所以单单只有num1[l][k]肯定是不够的,那么我们怎么样去“补全"这个区间呢?我们可以从r- 2^k 出发查找到r,那么这样就可保证覆盖了整个查找区间,如图所示。

在这里插入图片描述

就是这个样子,推荐大家一个例题

#CODE

#include<iostream>
#include<cstdio>
#include<cmath> 
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int a[50001];
int l,r; 
int num1[50001][51],num2[50001][51];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
      scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	  num1[i][0]=a[i],num2[i][0]=a[i];
	int t=log(n)/log(2)+1;
    for(int j=1;j<t;j++)
    for(int i=1;i<=n-(1<<j)+1;i++)
      num1[i][j]=max(num1[i][j-1],num1[i+(1<<(j-1))][j-1]),num2[i][j]=min(num2[i][j-1],num2[i+(1<<(j-1))][j-1]);
    for(int i=1,k;i<=m;i++)
      {
      	scanf("%d%d",&l,&r);
      	k=log(r-l+1)/log(2);
      	printf("%d\n",max(num1[l][k],num1[r-(1<<k)+1][k])-min(num2[l][k],num2[r-(1<<k)+1][k]));
	  }
	return 0;
}
<think>好的,用户想了解C++中ST的实现与用法。首先,我需要回忆ST的基本概念和原理。ST,也就是稀疏,主要用于处理静态数组的区间最值查询问题。它的核心思想是预处理和动态规划,通过构建一个二维数组来存储不同区间的预处理结果,从而在查询时以O(1)的时间复杂度得到结果。 接下来,我应该整理ST的实现步骤。首先,预处理阶段需要确定log2,方便快速计算区间长度对应的指数。然后,构建ST的结构,使用动态规划的方法填充每个起点和区间长度的最大值或最小值。这里要注意的是,ST适用于静态数据,一旦数据变动,就需要重新构建,所以应用场景通常是数据不变的情况。 然后,考虑用户可能的疑问点。例如,ST的构建时间复杂度是O(n log n),而查询是O(1),这比线段树的O(n)构建和O(log n)查询更有优势,但ST不支持动态更新。用户可能想知道如何在实际应用中选择合适的数据结构,比如静态场景下ST更高效,动态则需要线段树或树状数组。 接下来,我需要参考提供的引用内容。引用[2]提到稀疏用于静态数组,预处理时间O(n logn),查询O(1)。引用[3]和[4]提供了C++实现的模板,其中提到可以修改查询操作为最大值或其他操作。因此,在代码示例中,应该展示如何根据需求调整最大值或最小值,并解释代码中的关键部分,比如log_table的初始化、ST的构建和查询方法。 用户可能还会关心ST的应用场景,比如在RMQ(区间最值查询)问题中的使用,如LeetCode上的题目或竞赛编程中的常见问题。需要举例说明,比如滑动窗口中的最大值或者需要频繁查询静态数组区间最值的场景。 此外,要注意代码的正确性和可读性。给出的代码示例应该包含构造函数处理输入数组,预处理log_table,填充st,以及查询函数的实现。同时,要指出代码中可能需要注意的地方,比如数组索引从1开始还是0开始,这会影响log_table的计算和区间划分。 最后,生成相关问题时要覆盖ST的优化、与其他数据结构的比较、应用实例以及可能的时间复杂度分析。这些问题可以帮助用户进一步深入理解ST的相关知识。</think>### C++ ST实现与用法详解 #### 一、ST基础概念 ST(Sparse Table)是用于**静态数组区间最值查询**(Range Minimum/Maximum Query, RMQ)的高效数据结构。其核心特点是通过$O(n \log n)$的预处理时间,实现$O(1)$时间复杂度的查询[^2]。 #### 二、实现步骤 1. **预处理log** ```cpp vector<int> log_table; void init_log(int n) { log_table.resize(n + 1); for (int i = 2; i <= n; ++i) log_table[i] = log_table[i/2] + 1; } ``` 2. **构建ST** ```cpp vector<vector<int>> st; void build(const vector<int>& arr) { int n = arr.size(); int k = log2(n); st.resize(n, vector<int>(k+1)); for (int i = 0; i < n; ++i) st[i][0] = arr[i]; for (int j = 1; j <= k; ++j) for (int i = 0; i + (1<<j) <= n; ++i) st[i][j] = max(st[i][j-1], st[i + (1<<(j-1))][j-1]); } ``` 3. **区间查询** ```cpp int query(int l, int r) { int len = r - l + 1; int k = log_table[len]; return max(st[l][k], st[r - (1<<k) + 1][k]); } ``` #### 三、关键特性 1. **适用场景**:静态数据集的多次查询(如基因序列分析、地理信息系统中地形高程查询) 2. **时间复杂度**: - 预处理:$O(n \log n)$ - 查询:$O(1)$ 3. **空间复杂度**:$O(n \log n)$ #### 四、应用示例 ```cpp int main() { vector<int> data = {3, 1, 4, 5, 2, 9}; init_log(data.size()); build(data); cout << query(0, 2) << endl; // 输出4(区间[0,2]最大值) cout << query(3, 5) << endl; // 输出9 } ``` #### 五、与同类结构对比 | 数据结构 | 预处理时间 | 查询时间 | 动态更新 | |----------|------------|----------|----------| | ST | $O(n \log n)$ | $O(1)$ | ❌ | | 线段树 | $O(n)$ | $O(\log n)$ | ✔️ | | 树状数组 | $O(n)$ | $O(\log n)$ | ✔️ |
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值