0

How do I find/store maximum/minimum of all possible non-empty sub-arrays of an array of length n?

I generated the segment tree of the array and the for each possible sub array if did query into segment tree but that's not efficient. How do I do it in O(n)?

P.S n <= 10 ^7

For eg. arr[]= { 1, 2, 3 };  // the array need not to be sorted

sub-array     min      max
{1}            1        1
{2}            2        2
{3}            3        3
{1,2}          1        2
{2,3}          2        3
{1,2,3}        1        3
suraj bora
  • 61
  • 1
  • 10
  • Is the array sorted? – NathanOliver Aug 13 '15 at 02:05
  • 1
    Please provide sample data (even if smaller than real) and expected results. – Yakk - Adam Nevraumont Aug 13 '15 at 02:46
  • Are you looking for an algorithm or C++ code? If you're looking for an algorithm the language shouldn't matter and perhaps the `C++` tag should be removed. – skyking Aug 13 '15 at 05:59
  • Depending on exactly what you want you may or may not have reason to expect `O(n)`. If you just want the set of possible maximas and minimas you just have to stuff your elements into a set, that's `O(n)`, if you on the other hand want to enumerate all non-empty sub-arrays with the corresponding maximum/minimum you will have to confront the fact that the sub-arrays are `O(n^2)` in number, each of them taking some time to find max/min for, so at least `O(n^2)`, possibly more. – skyking Aug 13 '15 at 06:09
  • What's a subarray? A collection of *consecutive* entries? Or not? Please edit the question. – Aaron McDaid Aug 13 '15 at 17:15
  • Updated the problem. Sorry for inconvenience. – suraj bora Aug 14 '15 at 10:38

5 Answers5

2

I don't think it is possible to store all those values in O(n). But it is pretty easy to create, in O(n), a structure that makes possible to answer, in O(1) the query "how many subsets are there where A[i] is the maximum element".

Naïve version:

Think about the naïve strategy: to know how many such subsets are there for some A[i], you could employ a simple O(n) algorithm that counts how many elements to the left and to the right of the array that are less than A[i]. Let's say:

A = [... 10 1 1 1 5 1 1 10 ...]

This 5 up has 3 elements to the left and 2 to the right lesser than it. From this we know there are 4*3=12 subarrays for which that very 5 is the maximum. 4*3 because there are 0..3 subarrays to the left and 0..2 to the right.

Optimized version:

This naïve version of the check would take O(n) operations for each element, so O(n^2) after all. Wouldn't it be nice if we could compute all these lengths in O(n) in a single pass?

Luckily there is a simple algorithm for that. Just use a stack. Traverse the array normally (from left to right). Put every element index in the stack. But before putting it, remove all the indexes whose value are lesser than the current value. The remaining index before the current one is the nearest larger element.

To find the same values at the right, just traverse the array backwards.

Here's a sample Python proof-of-concept that shows this algorithm in action. I implemented also the naïve version so we can cross-check the result from the optimized version:

from random import choice
from collections import defaultdict, deque

def make_bounds(A, fallback, arange, op):
    stack = deque()
    bound = [fallback] * len(A)
    for i in arange:
        while stack and op(A[stack[-1]], A[i]):
            stack.pop()
        if stack:
            bound[i] = stack[-1]
        stack.append(i)
    return bound

def optimized_version(A):
    T = zip(make_bounds(A, -1, xrange(len(A)), lambda x, y: x<=y), 
            make_bounds(A, len(A), reversed(xrange(len(A))), lambda x, y: x<y))

    answer = defaultdict(lambda: 0)
    for i, x in enumerate(A):
        left, right = T[i]
        answer[x] += (i-left) * (right-i)
    return dict(answer)

def naive_version(A):
    answer = defaultdict(lambda: 0)
    for i, x in enumerate(A):
        left = next((j for j in range(i-1, -1, -1) if A[j]>A[i]), -1)
        right = next((j for j in range(i+1, len(A)) if A[j]>=A[i]), len(A))
        answer[x] += (i-left) * (right-i)
    return dict(answer)


A = [choice(xrange(32)) for i in xrange(8)]    
MA1 = naive_version(A)
MA2 = optimized_version(A)

print 'Array:    ', A
print 'Naive:    ', MA1
print 'Optimized:', MA2
print 'OK:       ', MA1 == MA2
avamsi
  • 379
  • 2
  • 15
Juan Lopes
  • 9,563
  • 2
  • 23
  • 41
  • Great this is what i was finding. But the problem is that i cannot understand the python code. – suraj bora Aug 14 '15 at 11:08
  • @Juan Lopes but don't you think that popping elements from stack will also take O(n) operations then complexity becomes O(n^2). –  Aug 15 '15 at 05:39
  • @user3396302 You can't pop more elements than entered the stack. Each element enter the stack exactly once (only one append) per direction, by the algorithm. There can be only O(n) operations in the stack. – Juan Lopes Aug 15 '15 at 05:49
1

I don't think it is possible to it directly in O(n) time: you need to iterate over all the elements of the subarrays, and you have n of them. Unless the subarrays are sorted.

You could, on the other hand, when initialising the subarrays, instead of making them normal arrays, you could build heaps, specifically min heaps when you want to find the minimum and max heaps when you want to find the maximum.

Building a heap is a linear time operation, and retrieving the maximum and minimum respectively for a max heap and min heap is a constant time operation, since those elements are found at the first place of the heap.

Heaps can be easily implemented just using a normal array.

Check this article on Wikipedia about binary heaps: https://en.wikipedia.org/wiki/Binary_heap.

Community
  • 1
  • 1
nbro
  • 12,226
  • 19
  • 85
  • 163
0

I do not understand what exactly you mean by maximum of sub-arrays, so I will assume you are asking for one of the following

  1. The subarray of maximum/minimum length or some other criteria (in which case the problem will reduce to finding max element in a 1 dimensional array)
  2. The maximum elements of all your sub-arrays either in the context of one sub-array or in the context of the entire super-array

Problem 1 can be solved by simply iterating your super-array and storing a reference to the largest element. Or building a heap as nbro had said. Problem 2 also has a similar solution. However a linear scan is through n arrays of length m is not going to be linear. So you will have to keep your class invariants such that the maximum/minimum is known after every operation. Maybe with the help of some data structure like a heap.

0

Assuming you mean contiguous sub-arrays, create the array of partial sums where Yi = SUM(i=0..i)Xi, so from 1,4,2,3 create 0,1,1+4=5,1+4+2=7,1+4+2+3=10. You can create this from left to right in linear time, and the value of any contiguous subarray is one partial sum subtracted from another, so 4+2+3 = 1+4+2+3 - 1= 9.

Then scan through the partial sums from left to right, keeping track of the smallest value seen so far (including the initial zero). At each point subtract this from the current value and keep track of the highest value produced in this way. This should give you the value of the contiguous sub-array with largest sum, and you can keep index information, too, to find where this sub-array starts and ends.

To find the minimum, either change the above slightly or just reverse the sign of all the numbers and do exactly the same thing again: min(a, b) = -max(-a, -b)

mcdowella
  • 18,736
  • 2
  • 17
  • 24
-1

I think the question you are asking is to find the Maximum of a subarry. bleow is the code that cand do that in O(n) time.

int maxSumSubArr(vector<int> a)
{
    int maxsum = *max_element(a.begin(), a.end());
    if(maxsum < 0) return maxsum;
    int sum = 0;
    for(int i = 0; i< a.size; i++)
    {
      sum += a[i];
      if(sum > maxsum)maxsum = sum;
      if(sum < 0) sum = 0;
    }
    return maxsum;
}

Note: This code is not tested please add comments if found some issues.

  • `int maxsum = max(a.begin(), a.end());` doesn't compile (you take the maximum of 2 iterators (which is `a.end()`) and try to convert it into `int`). – Jarod42 Aug 13 '15 at 07:41
  • Close, but OP said "non-empty subarrays" so if the sum goes below 0 you can't assume you can make a sum of 0 – harold Aug 13 '15 at 11:12
  • @Jarod42 Yes that was a mistake I think i have corrected it thanks for pointing out the same. – Hariprasadmce Aug 13 '15 at 16:58
  • This one returns the non-empty subarry. if all the elements in the array are -ve then return the maximum number in the array or else the below loop will do the job. – Hariprasadmce Aug 13 '15 at 16:59