【算法讲解】区间极值查询 RMQ(Range Maximum/Minimum Query)

文章讨论了在计算机编程中处理区间极值问题的不同算法,包括静态数组和动态数组的情况。对于静态数组,提到了单调栈、稀疏表、并查集等方法,而动态数组则涉及线段树、分块和树状数组等数据结构。每种算法都有其适用场景、时间复杂度和优缺点。

前言

区间极值问题在计算机编程领域中常见的问题,其描述为:

给一个数组 A[1…N]A[1 \dots N]A[1N],然后有大量的询问,询问某个区间的 [L,R],1≤L≤R≤N[L,R], 1\le L\le R\le N[L,R],1LRN 的最大/最小值(或者下标)
例如,数组 A=[0,5,2,5,4,3,1,6,3]A=[0,5,2,5,4,3,1,6,3]A=[0,5,2,5,4,3,1,6,3],查询 A[3…8]=[2,5,4,3,1,6]A[3 \dots 8]=[2,5,4,3,1,6]A[38]=[2,5,4,3,1,6] 的最小值 为 A[7]=1A[7]=1A[7]=1 (下标从1开始)

变种 & 算法

在此基础上,又有不同的常见变种,根据不同的变种,我们有对应的复杂度/写法简易度的算法来解决:

O(A)∼O(B)O(A) \sim O(B)O(A)O(B) 来表示预处理需要的复杂度AAA和每次查询需要的复杂度BBB
比如最朴素的做法:不需要预处理,每次都从 L 遍历 到 R,它的时间复杂度就是 O(1)∼O(n)O(1) \sim O(n)O(1)O(n)。这个做法就不介绍了,有手就能写,但显然是最差的方案。

下面介绍一些适用于不同场景的算法

静态数组

如果 AAA 是静态数组,既 A 的元素不会动态变化,有如下一些算法

单调栈/队列

适用于: 计算每个元素左边/右边(k 个元素)的最大最小值,既询问的区间只会是[1,i][1, i][1,i][i,n][i,n][i,n] 或者 [i−k,i][i-k,i][ik,i][i,i+k][i,i+k][i,i+k]
时间复杂度:O(n)∼O(1)O(n) \sim O(1)O(n)O(1)
优势:优秀的复杂度,实现简单
劣势:适用场景有限

稀疏表 Sparse Table

适用于: 无限制
时间复杂度:O(nlog⁡n)∼O(1)O(n \log n) \sim O(1)O(nlogn)O(1)
优势:优秀的查询速度,实现简单
劣势:需要 O(nlog⁡n)O(n \log n)O(nlogn) 的空间,这个还好,其实不算缺点
备注:静态 RMQ 问题的最适合方案
也可以用于区间求和,不过询问的复杂度就不是 O(1)O(1)O(1) ,而是 O(log⁡n)O(\log n)O(logn)

并查集 Disjoint Set Union

适用于:所有询问都已经知道,离线查询
时间复杂度:O(n)∼O(1)O(n) \sim O(1)O(n)O(1)
优势:优秀的查询速度,实现简单
劣势:只适用与离线场景
备注:很神奇的技巧(由 AmirReza Poorakhavan 发明,所以也称之为 arpa’s trick)这里先简单说一下原理:将查询按照右端点桶排序,从左到右遍历,每个元素的父节点设置为右边已经遍历的最小元素,然后查询左端点的根节点即可

分块树 Sqrt Tree

适用于:无限制
时间复杂度:O(nlog⁡log⁡n)∼O(1)O(n\log \log n) \sim O(1)O(nloglogn)O(1)
优势:优秀的复杂度
劣势:超复杂的实现

最近公共祖先 LCA 的应用

适用于:无限制
时间复杂度:O(n)∼O(1)O(n) \sim O(1)O(n)O(1)
优势:优秀的复杂度
劣势:超复杂的实现

动态数组:

对于 A 是动态的情况,有如下几个算法:

线段树 Segment Tree

适用于:无限制
时间复杂度:O(n)∼O(log⁡n)O(n) \sim O(\log n)O(n)O(logn)
空间复杂度:O(n)O(n)O(n)
优势:优秀的
劣势:实现略复杂
备注:静态 RMQ 问题的最适合方案
除了 RMQ,大部分区间操作、询问都可以用线段树解决

分块 Sqrt-decomposition

适用于:无限制
时间复杂度:O(n)∼O(n)O(n) \sim O(\sqrt n)O(n)O(n)
优势:实现简单
劣势:复杂度较差

树状数组 Fenwick Tree

适用于:查询是 [1,R][1,R][1,R]
时间复杂度:O(nlog⁡n)∼O(log⁡n)O(n \log n) \sim O(\log n)O(nlogn)O(logn)
优势:实现简单
劣势:适用场景有限
备注:除了 RMQ,更适用于区间求和,区间求异或,因为这两个操作顺序无关

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值