树状数组求区间最值
树状数组(Binary Index Tree)利用二进制的一些性质巧妙的划分区间,是一种编程,时间和空间上都十分理想的求区间和的算法,同样我们可以利用树状数组优美的区间划分方法来求一个序列的最值
约定以 num[] 表示原数组, 以 idx[] 表示索引数组, Lowbit(x)=x&(-x)
树状数组求和时通过构造数组 idx[] 使 idx[k]=sum(num[tk]), tk [k-Lowbit(k)+1,k], 使用同样的方法构造最值索引数组:
以最大值为例, 先讨论询问过程中不对数组做任何修改的情况, 用 idx[k] 记录 [k-Lowbit(k)+1,k] 区间内的最大值, 可以仿照求和时的方法得到:
1 2 3 4 5 6 7 | void Init( int n){ for ( int i=1;i<=n;i++){ for ( int j=i;j<=n;j+=Lowbit(j)){ idx[j]=MAX(idx[j],num[i]); } } } |
这种方法在每次调用该函数前都必须对数组进行初始化, 这样对于数据范围比较大的时候不是很优美, 这样我们可以改为:
1 2 3 4 5 6 7 8 | void Init( int n){ for ( int i=1;i<=n;i++){ idx[i]=num[i]; for ( int j=1;j<Lowbit(i);j<<=1){ idx[i]=MAX(idx[i],idx[i-j]); } } } |
这样, 在更新到第k个数时, 所有 t(t<k) 都已经是正确的值了, 不存在上面那个函数的情况了
然后再来看查询的问题, 对于区间 [l,r] 把该区间转化为多个的小区间再进行求最值, 方法是从后往前对每一个索引数的范围进行判断, 如在进行到第k项时,该数控制的范围是 [k-Lowbit(k)+1,k], 如果k-Lowbit(k)+1在所求的范围内的话则将该区间的最值加入最值的判断,然后转至地k-Lowbit(k),否则的话就只对第k个数进行最值判断,然后转至k-1,具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 | int Query( int l, int r){ int ans=num[r]; while ( true ){ ans=MAX(ans,num[r]); if (r==l) break ; for (r-=1;r-l>=Lowbit(r);r-=Lowbit(r)){ ans=MAX(ans,idx[r]); } } return ans; } |
该查询的复杂度为log(n)
st算法的复杂度 O(nlog(n)) / O(1) , 线段树为 O(nlog(n)) / (log(n)),树状数组 O(<nlog(n)) / O(log(n))
空间复杂度 st 为 O(nlog(n)), 线段树 O(n),常数较大 , 树状数组是 O(n)
编程上 st 和 树状数组 都比较容易实现,线段树代码较长
另外线段树灵活性较大
PKU 3264 题:
st Memory: 6372K Time: 1250MS 964B
BIT Memory: 716K Time: 1282MS 933B
SegTree 未测,要比st更大
然后我们可以进一步扩展到边查询边修改的情况
每次直接去更新父亲节点自然是不行的, 为了维护索引数组的正确性,我们在对每个父亲节点进行更新时都要查询他的所有儿子节点,在其中取最优值, 得到代码如下:
1 2 3 4 5 6 7 8 9 | void Modify( int p, int v, int n){ num[p]=v; for ( int i=p;i<=n;i+=Lowbit(i)){ idx[i]=v; for ( int j=1;j<Lowbit(i);j<<=1){ idx[i]=MAX(idx[i],idx[i-j]); } } } |
复杂度为 O(log^2(n)), HDU 1754 I hate it 437MS 1776K
另外,该方法还有一个减枝, 很容易想到,在求最大值时,当某个值更新的值大于原值的时候是没有必要再去查询儿子节点的,所以内部循环可加一个判断来判定是否需要扫描儿子节点,可能是数据问题,该题时间并没有大的变化
转载请注明出处:http://www.cnblogs.com/ambition/archive/2011/04/06/bit_rmq.html
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp
· 一个基于 .NET 开源免费的异地组网和内网穿透工具
· 《HelloGitHub》第 108 期
· Windows桌面应用自动更新解决方案SharpUpdater5发布
· 我的家庭实验室服务器集群硬件清单