8

I have an algorithm question:

Given an array(assuming all the elements are intergers) of size N, find the largest drop(not necessarily continuous) : max{array[i]-array[j]} with a constraint: i>j .

The simple solution is that two loops and go through all possible values of i and j,but the time complexity is O(n*n).

And an improved solution I think is to map the indexes of the array first, sort the array and go through the array to find the largest drop. This complexity is O(nlogn).

Is there a solution with linear time complexity? And how?

PS: I once thought a linear solution: create two extra arrays, one is to record the max value of the given array from the beginning to the end, and the other to the min value from the end to the beginning.Then, go through two array one pass. However, someone thought that not correctly and taking up too big space. So I want to know a better solution. – lijuanliu

lijuanliu
  • 83
  • 1
  • 6
  • 1
    Ah, a question from course homework. Shouldn't you better solve that yourself? – arkascha Feb 22 '13 at 08:32
  • @arkascha: I once thought a linear solution: create two extra arrays, one is to record the max value of the given array from the beginning to the end, and the other to the min value from the end to the beginning.Then, go through two array one pass. However, someone thought that not correctly and taking up too big space. So I want to know a better solution. – lijuanliu Feb 22 '13 at 08:41
  • You didn't catch my point ;-) – arkascha Feb 22 '13 at 08:44
  • Hardly fair to assume this question is for homework. This is a relatively common coding challenge question, and also a classic example for showing how clever algorithm design can drastically improve run-time. – Bash Jan 20 '19 at 19:16

5 Answers5

6

O(n) solution without extra space:

public int maxDrop(int[] a) {
    int max = a[0];
    int maxDrop = -1;
    for (int i = 0; i < a.length; i++) {
        if (max < a[i]) {
            max = a[i];
        }
        else {
            int drop = max - a[i];
            maxDrop = Math.max(maxDrop, drop);
        }
    }
    return maxDrop;
}
alampada
  • 2,329
  • 1
  • 23
  • 18
5

You need to keep track of two things:

The maximum number you have seem up to element i, and the biggest drop you have seem with respect to the biggest number (i.e. the maximum number before i, minus the element i). This will be O(n) in time and O(1) in space.

This problem is exactly the "stock buying/selling" interview question, solution can be found here: Maximum single-sell profit

Community
  • 1
  • 1
HeavenAgain
  • 435
  • 1
  • 6
  • 14
4

Create new 2 arrays:

max[i] = max { arr[0], arr[1], ..., arr[i] }
min[i] = min { arr[n-1], arr[n-2], ..., arr[i] }
(max is the maximum from first to i, min is the minimum from i to last)

Now, iterate the auxilary arrays and find the maximum difference max[i] - min[i]

This requires 3 iterations overall and is thus O(n).

Proof of correctness (guidelines):

Let the biggest drop be from index i to index j, where i<j:

  1. Then, max[i] >= arr[j] (because we already passed it), and also min[i] <= arr[i] - thus max[j] - min[j] >= arr[i] - arr[j], and the answer provided by the algorithm is at least as good as the optimal.
  2. Also, since the biggest drop is i,j, there cannot be any k<j such that arr[k] < arr[i], because then the biggest drop will be from arr[k]to arr[j]. Similary - there cannot be k>j such that arr[k] < arr[j], for the same reasons - and thus max[j]-min[j] <= arr[i] - arr[j]

From the above we can conclude that max[j]-min[j] = arr[i] - arr[j]. All is left for a formal complete proof is show that for each k you get max[k]-min[k] <= max[j] - min[j], and this is indeed the case, otherwise there are some u<k, v>k such that max[k]=arr[u], min[k]=arr[v], and you get that arr[u] - arr[v] > arr[i] - arr[j], which contradicts the fact that i,j is the biggest drop.

QED

amit
  • 175,853
  • 27
  • 231
  • 333
  • @lijuanliu Yes, `O(n)` extra space – amit Feb 22 '13 at 09:32
  • I have an idea, is an auxilary array enough? (Just keeping the "min" array) – lijuanliu Feb 22 '13 at 09:42
  • @lijuanliu Basically you can modify the approach by going from last to first, remember the minimum, and for each element check if `arr[i]-min >= max_so_far` - if it is, it is the new max. It is easy to see that it is actually an `O(1)` space optimization of the previous `O(n)` space version, and the proof will work very similarly. – amit Feb 22 '13 at 09:52
-1

I can only think of O(nlogn) but this one even though same complexity should be faster then sorting and finding the largest drop.

What you can do is to calculate the differences of the consecutive numbers in O(n) and then the problem would be reduced to finding the MAX(sum) of continuous sub array which would be O(nlogn)

Techmonk
  • 1,459
  • 12
  • 20
  • This doesn't work when there is a k between i+1 and j with array[k] > array[k-1] – Jens Schauder Feb 22 '13 at 08:41
  • Yes it will as you can add (-) number and reduce the sum. That is why it is O(nlogn ) and not O(n) you need to iterate through the whole array. And seriously why the down-vote? – Techmonk Feb 22 '13 at 08:48
-1
public static int maxDrop(int[]array){

    int maxDrop =0,drop=0,min=array[0];

    for(int i=1;i<array.length;i++){

        if(array[i]<min){
            min=array[i];
        }

        drop = array[i]-min;

        if(drop>maxDrop){
            maxDrop=drop;
        }

    }

    System.out.println(maxDrop);
    return maxDrop;

}
sreeprasad
  • 3,242
  • 3
  • 27
  • 33
  • I think that there is a bug: for input { 5, 21, 3, 22, 12, 7, 26, 14}, the code above returns 23 instead of 18. – alampada Aug 22 '15 at 22:32