前言
区间极值问题在计算机编程领域中常见的问题,其描述为:
给一个数组 A [ 1 … N ] A[1 \dots N] A[1…N],然后有大量的询问,询问某个区间的 [ L , R ] , 1 ≤ L ≤ R ≤ N [L,R], 1\le L\le R\le N [L,R],1≤L≤R≤N 的最大/最小值(或者下标)
例如,数组 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[3…8]=[2,5,4,3,1,6] 的最小值 为 A [ 7 ] = 1 A[7]=1 A[7]=1 (下标从1开始)
变种 & 算法
在此基础上,又有不同的常见变种,根据不同的变种,我们有对应的复杂度/写法简易度的算法来解决:
用
O
(
A
)
∼
O
(
B
)
O(A) \sim O(B)
O(A)∼O(B) 来表示预处理需要的复杂度
A
A
A和每次查询需要的复杂度
B
B
B
比如最朴素的做法:不需要预处理,每次都从 L 遍历 到 R,它的时间复杂度就是
O
(
1
)
∼
O
(
n
)
O(1) \sim O(n)
O(1)∼O(n)。这个做法就不介绍了,有手就能写,但显然是最差的方案。
下面介绍一些适用于不同场景的算法
静态数组
如果 A A A 是静态数组,既 A 的元素不会动态变化,有如下一些算法
单调栈/队列
适用于: 计算每个元素左边/右边(k 个元素)的最大最小值,既询问的区间只会是
[
1
,
i
]
[1, i]
[1,i]、
[
i
,
n
]
[i,n]
[i,n] 或者
[
i
−
k
,
i
]
[i-k,i]
[i−k,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
(
n
log
n
)
∼
O
(
1
)
O(n \log n) \sim O(1)
O(nlogn)∼O(1)
优势:优秀的查询速度,实现简单
劣势:需要
O
(
n
log
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
(
n
log
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
(
n
log
n
)
∼
O
(
log
n
)
O(n \log n) \sim O(\log n)
O(nlogn)∼O(logn)
优势:实现简单
劣势:适用场景有限
备注:除了 RMQ,更适用于区间求和,区间求异或,因为这两个操作顺序无关