查找给定范围的未排序数组中的最大元素[允许预处理]?

时间:2016-06-04 10:13:45

标签: algorithm dynamic-programming

如果允许预处理,在给定范围的未排序数组中找到最大元素的最快方法是什么。

我们有初始数组A = {3,2,4,5,1},我们需要对它进行预处理,然后我们回答q个查询。

查询示例,如果查询中指定的范围是[0,2],则此范围中的最大元素为4.

2 个答案:

答案 0 :(得分:5)

<强>解决方案:

我找到了三种解决方案,每种解决方案在某些条件下都有用。他们是:

  1. 构建矩阵(我的原始解决方案)
  2. 构建二叉树
  3. 不要预处理
  4. 以下讨论各自的优点和缺点。

    <强> 矩阵

    要点:

    构建一个二维数组MM[i][j]A[i:j]中的最大元素。

    分析:

    当您执行 O(n 2 / log n)查询并且至少 O(n 2)时,此选项最佳空间。

    Space requirement: O(n^2)
    Preprocessing time: O(n^2)
    Query time: O(1)
    

    伪代码:

    class Range {
      private M
    
      function Preprocess(A) {
        for(i from 0 to n)
          M[i][i] = A[i]
          for(j from i+1 to n)
            M[i][j] = max(M[i][j-1], A[j])
      }
    
      function Query(i, j) {
        return M[i][j]
      }
    }
    

    二叉树:

    要点:

    这是最复杂的选择。您构建了一个树T,使其

    • T.rootA[0:n]
    • 中的最大值
    • T.root.leftA[0:n/2]
    • 中的最大值
    • T.root.rightA[n/2+1:n]
    • 中的最大值

    然后最多有 O(log n)个节点,表示任何给定范围的最大值。

    分析:

    此选项最适用于 a)您仅限于 O(n)空间或 b)您执行的操作少于< em> O(n 2 / log n)查询。

    Space requirement: O(n)
    Preprocessing time: O(n)
    Query time: O(log n)
    

    伪代码:

    class Range {
      private T
    
      function Preprocess(A) {
        T = BinaryTree({leaves: A})
    
        ################################################
        # loop over all levels of the tree, 
        #  starting just above the leaves
        ################################################
        foreach(T.levels as level)
          foreach(level as node)
            node.value = max(node.left.value, node.right.value)
            node.leftBound = node.left.leftBound
            node.rightBound = node.right.rightBound
      }
    
      function Query(i, j) {
        nodes = []
        currentLeft = T.root
        currentRight = T.root
    
        ################################################
        # search for the left bound of the range, 
        #  keeping any nodes fully contained in the range
        ################################################
        while(currentLeft.leftBound < i)
          if(currentLeft.right.leftBound <= i)
            currentLeft = currentLeft.right
          else
            # check if currentLeft.right is fully in the range
            if(currentLeft.right.rightBound <= j)
              nodes.push(currentLeft.right)
            currentLeft = currentLeft.left
    
        if(currentLeft.rightBound <= j)
          nodes.push(currentLeft)
    
        ################################################
        # repeat the above to find the right bound
        ################################################
        while(currentRight.rightBound > j)
          ...
    
        if(currentRight.leftBound >= i)
          nodes.push(currentRight)
    
        ################################################
        # find the maximum value in nodes
        ################################################
        m = -inf
        foreach(nodes as node)
          m = max(m, node.value)
        return m
      }
    }
    

    (请注意,给定的伪代码可能会将给定节点添加到nodes两次,从而导致冗余工作。但是,它总是不会添加超过 O(log n)节点,我不想让伪代码进一步复杂化。)

    不要预处理:

    要点:

    如果您没有进行太多查询,则不应浪费时间进行预处理。

    分析:

    此选项最适合您进行非常少的查询(围绕 O(1))。

    Space requirement: O(n)
    Preprocessing time: O(1)
    Query time: O(n)
    

    伪代码:

    class Range {
      private M
    
      function Preprocess(A) {
        M = A
      }
    
      function Query(i, j) {
        m = M[i]
        for(k = i+1; k < j; ++k)
          m = max(m, M[k])
        return m
      }
    }
    

答案 1 :(得分:0)

以下是来自@Kittsil's answer的动态编程解决方案(O(n**2)预处理,O(1)用于“最大范围内”查询)的实现。要在Python中查找M[start][end] == max(A[start:end])

#!/usr/bin/env python
A = [3, 2, 4, 5, 1]
n = len(A)

M = [[None] * (n + 1) for _ in range(n)]
for i in range(n):
    for j in range(i, n):
        M[i][j + 1] = A[j] if i == j else max(M[i][j], A[j])

测试:

for start in range(n):
    for end in range(start + 1, n + 1):
        assert M[start][end] == max(A[start:end])