使用小于O(N ^ 2)的内存,在i-j范围内找到小于O(j-i)的最小值

时间:2013-03-08 09:35:10

标签: c++ algorithm complexity-theory minimum

假设有一个包含N个整数的大数组:

const unsigned N = 1e12;
int array[N] = { 1, 3 , 8, -5, 4, 3, -1 -6, 6, ....., N};

应该多次查询不同i j点范围内的最小元素。返回最小值的复杂度应小于O(j-i),并且应使用小于O(N ^ 2)的内存来解决问题。

如何做到这一点?

4 个答案:

答案 0 :(得分:3)

RMQ以下列方式运作:

我们保持一个数组M [N] [logN],其中M [i] [j]表示从i开始并且长度为2 ^ j的最小范围数。为了填充该数组,首先我们计算所有M [i] [0]值,它们都等于M [i] [0] = A [i](A [i]是原始数组)。之后,通过感应,每个M [i] [j]将等于 min(M [i] [j-1],M [i +(1 <&lt;(j-1))] [ j - 1])即我们通过将其左边和右边的最小值取得更长的间隔来获得值。正确的部分,应该在前一步计算,因为我们从最短到最长的间隔。

之后,要获得[a..b]间隔中的最小值,您需要找到最大的P,这样2 ^ P不会超过间隔[a..b]的长度。并且答案将是 min(M [a] [P],M [b - (1

答案 1 :(得分:2)

对于静态数组,正如您所提到的,最快的解决方案是使用O(n)preproc的O(1)。但在实践中,您可能希望使用以下方法之一,这也适用于动态数组,在我看来更容易理解和编码:

  1. 将数组划分为sqrt(n)个部分,每个部分都包含sqrt(n)个元素,并为每个细分存储最小值。每个(i,j)将完全包含一些这些段加上左右两个元素。传递这些元素并存储段的答案以找到最小值。这需要O(n)preproc,O(sqrt(n))查询,O(sqrt(n))更新和O(sqrt(n))存储器。编码也很容易。
  2. 在数组上构建minumum segment tree。这为查询/更新提供了O(logn),O(n)memory / preproc。

答案 2 :(得分:1)

对于那些了解俄语的人来说,这就是解决方案:http://e-maxx.ru/algo/rmq 对于那些不懂俄语的人,抱歉,我还没找到什么。如果有人会找到,请编辑我的答案。

答案 3 :(得分:1)

一个简单的解决方案是创建一个二维数组,其中一个条目[i,j]存储范围arr [i..j]中的最小值。现在可以在O(1)时间内计算给定范围的最小值,但预处理需要O(n ^ 2)时间。此外,这种方法需要O(n ^ 2)额外空间,这对于大型输入阵列可能会变得很大。

另一个解决方案是构建一个Segment树。可以使用Segment树在适度的时间内进行预处理和查询。对于分段树,预处理时间为O(n),范围最小查询的时间为O(Logn)。所需的额外空间是O(n)来存储段树。

段树的表示

  1. 叶子节点是输入数组的元素。
  2. 每个内部节点代表其下所有叶子的最小值。
  3. 这里详细解释了Segment树的构造:

    http://www.geeksforgeeks.org/segment-tree-set-1-range-minimum-query/