1

What is the fastest way to find the maximum element in the given range of unsorted array, if the preprocessing is allowed.

We have initial array A = {3, 2, 4, 5, 1} and we need to preprocess it, and then we answer the q queries.

Example for a query, if range specified in query is [0, 2], then the maximum element in this range is 4.

3
  • Can you explain the problem a bit more in depth ? When preprocessing, do you have knowledge of the range ? Or is that you have one step of preprocessing and then queries for the maximum within different ranges ? Commented Jun 4, 2016 at 10:24
  • Initially we have an array and we need to preprocess it and then we get queries to answer. Commented Jun 4, 2016 at 10:28
  • See wikipedia for the most efficient solutions to this problem. Commented Feb 7, 2018 at 22:26

2 Answers 2

7

Solutions:

I have found three solutions, each useful under certain conditions. They are:

  1. Build a matrix (my original solution)
  2. Build a binary tree
  3. Do not preprocess

Each of these have advantages and disadvantages discussed below.


Matrix:

Summary:

Build a two-dimensional array M such that M[i][j] is the maximum element in A[i:j].

Analysis:

This option is best when you are doing more than O(n2/log n) queries and have at least O(n2) space.

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

Pseudocode:

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]
  }
}

Binary tree:

Summary:

This is the most complex option. You build a tree T such that

  • T.root is the maximum value in A[0:n],
  • T.root.left is the maximum value in A[0:n/2],
  • T.root.right is the maximum value in A[n/2+1:n],
  • etc.

Then there are at most O(log n) nodes representing the maximum values for any given range.

Analysis:

This option is best when either a) you are limited to O(n) space or b) you are doing fewer than O(n2/log n) queries.

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

Pseudocode:

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
  }
}

(Note that the given pseudocode could potentially add a given node to nodes twice, causing redundant work. However, it will never add more than O(log n) nodes in total, and I didn't want to further complicate the pseudocode.)

Do not preprocess:

Summary:

If you aren't doing too many queries, you shouldn't waste time preprocessing.

Analysis:

This option is best when you will do very few queries (around O(1)).

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

Pseudocode:

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
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Can please give a simple pseudo code for how to create this 2D array
There are some simple optimizations you can make to the binary tree solution: 1) when the overlap between the query and a node is small enough (e.g. <= 8), just do a linear scan in the array. This massively reduces the number of nodes you need to create. 2) At search time, keep track of the 'best solution so far', and stop recursing into a subtree if that node has max < best yet. Sort the children of each node by max to make this more effective.
@user1502040 Those are good pruning techniques, but they do not affect the asymptotic complexity. Also, "massively" is a bit of an overstatement.
@Kittsil Points taken. However if you set the threshold at 8, that's already 8x fewer nodes, and you can easily go more aggressive and set it to e.g. 64 or even higher.
And with sorting children + pruning searching seems to take expected constant time for random array elements and random ranges, so it's a big win in practice.
0

Here's an implementation of the dynamic programming solution (O(n**2) preprocessing, O(1) for "max in range" query) from @Kittsil's answer. To find M[start][end] == max(A[start:end]) in Python:

#!/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])

Test:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.