RMQ问题的离线近线性算法

RMQ问题的离线近线性算法

北京市清华附中高逸涵

       RMQ问题是这样一种问题:给定一长度为n的数组A,回答询问RMQ(A,I,J),即A[i…j]之间的最小数的下标。在不引起误会的情况下,直接用RMQ(i,j)来表示这个询问。

       对于这一问题,存在一个非常好的在线算法Sparse Table,能在O(nlogn)-O(1)的时间复杂度解决问题。该算法记录了所有长度形如2i的所有询问的结果,对于一般的RMQ询问,取k=[log2(j-i+1)],则有:

                                     RMQ(i,j)=min{d[i,k],d[j-2^k+1,k]}

 

       其中d[i,j]表示从i开始长度为2j区间内的RMQ,有递推公式:

                                     d[i,j]=min{d[i,j-1],d[i+2^(j-1),j-1]}

 

       对于离线问题,我们希望做的更好。首先先确定RMQ问题的存储方式:采用链表存储,这样的好处是可以在O(1)的时间内得到以某个下标结尾的所有询问。

       算法是基于这样一个简单的性质:如果按照RMQ问题询问的区间结束位置处理,则对于A[i]和A[j],如果有j>i且A[j]<=A[i],则i不可能是后续询问的答案。

       因此,考虑建立一个栈,每次向栈顶插入一个数,插入前将栈中所有大于等于该数的数删除,很明显这样操作后栈中元素呈递增序列。这样的话,每次询问时在栈中二分查找起始下标的位置,如果栈中相邻的两个数的下标满足B[i]<start,B[j]>=start,则B[j]即为所求下标。

       这样,问题的算法复杂度总计为O(Qlogn),实际上栈中的数的个数一般来说远小于数组的大小,所以实际运行效率还是很快的。

       当然,这样的算法与Sparse Table相比并没有任何的优势。所以我们希望进一步改进算法。

       考虑问题中每个元素被询问时所给出的答案,我们发现它可以表示为以下的集合形式,为了便于说明,下面给出了算法运行的图表。

步骤1

 

步骤2

{1}{2}

步骤3

{1}{2}{0} ->{1}{2 0}->{1 2 0}

步骤4

{1 2 0}{3}

步骤5

{1 2 0}{3}{2} -> {1 2 0}{3 2}

步骤6

{1 2 0}{3 2}{6}

步骤7

{1 2 0}{3 2}{6}{0}->{1 2 0}{3 2}{6 0}->{1 2 0}{3 2 6 0}->{1 2 0 3 260}

{1}

表1(集合的代表元素用加粗字体表示)

       可以看到,每一次插入数即为新建一个集合,而删除一个数即将两集合合并,每次询问则为查询一个数所在的集合的代表元素。这正好是并查集所支持的各种操作。而并查集的每次操作的均摊复杂度接近O(1),所以RMQ的离线问题也可以在O(N+Q)的时间内解决,空间复杂度也是O(N+Q)。

       对于N和Q相差不大的问题来说,本文所述的离线无论时间和空间都远好于Sparse Table算法。这也是离线算法所能达到的一个非常好的复杂度了。

       值得一提的是,LCA问题的Tarjan算法即为本文所述算法在±1-RMQ问题中的特例。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值