Range Minimum Query (RMQ)

RMQ(Range Minimum Query) : 给定一个数组,求给定的两个索引(下标)间最小值元素的索引.

 

符号意义 :

假设一个算法有 f(n) 的预处理时间和g(n) 的查询时间.则这个算法的总的时间复杂度记为<f(n), g(n)>

 

记数组A 在索引ij 之间的最小值元素的索引为RMQA (i, j) .


例子:A[0,9]




对于给定的数组 A[0,N-1] , N 为元素个数

解法一 :动态规划

对于每对索引 (i , j) 存储值 RMQA (i, j) 在数组 M[0, N-1][0, N-1]

预处理函数如下:


算法时间复杂度为 <O(N2 ) , O(1)> ,空间复杂度为 O(N2 ) ,对于N比较大时,很耗内存

解法二Sparse Table ( 稀疏表 )

一个好的方法是对长度为 2k 的子数组进行预处理。保存一个数组 M[0, N-1][0, logN]M[i][j] 表示从一个下标从 i 开始,长度为 2j 的子数组中最小元素的下标

例子如下:




  为了计算 M[i][j] ,我们必须搜索这个区间的前半部分和后半部分.很明显,每一部分的长度为 2j - 1 。递推式如下:




预处理函数如下所示:



预处理后,让我们看看怎么计算 RMQA (i, j) . 思想就是选择出两个将区间 [i..j] 完全覆盖的小区间(预处理的 M ),然后取其较小值. 取 k = [log(j - i + 1)] . 为了计算 RMQA (i, j) 使用如下公式:


此算法总的时间复杂度为 <O(N logN), O(1)> .


解法三 :Segment Tree(线段树)

为了解决RMQ,还可以使用线段树.线段树能在对数时间内在数组区间上进行更新与查询。我们定义线段树在区间 [i, j] 上如下:
  • 第一个节点维护着区间 [i, j] 的信息。
  • if i<j , 那么左孩子维护着区间[i, (i+j)/2] 的信息,右孩子维护着区间[(i+j)/2+1, j] 的信息。
可知 N 个元素的线段树的高度 为 [logN] + 1(只有根节点的树高度为0) . 下面是区间 [0, 9] 的一个线段树:






线段树和堆有一样的结构, 因此如果一个节点编号为 x ,那么左孩子编号为 2*x 右孩子编号为 2*x+1 (在这里下标从1开始).

使用线段树解决RMQ问题,我们要维护一个数组 M[1, 2 * 2[logN] + 1 ]M[i] 维护着被分配给该节点的区间的最小值元素的下标。 该数组初始状态为 -1 . 树应该被以下函数初始化 ( b e 是当前区间的左右边界):



该函数反映的是树被构建的方法。 我们应该用 node = 1 , b = 0e  = N-1 来调用这个函数

现在开始查询. 如果我们想要找出区间 [i, j] 上的最小值的索引,我们应该使用下面这个简单的函数:




我们可以用 node = 1 , b = 0e = N - 1 来调用这个函数。 因为第一个节点的区间是 [0, N-1] .

很容易看出任何查询都能在时间 O(log N) 内完成。
使用线段树得到了一个 <O(N), O(logN)> 的算法.
线段树非常有用,不仅仅因为它可以用在解决RMQ问题上。它是一个非常灵活的数据结构,在范围搜索问题上有很多应用.







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值