3

I am writing the code for peak finding algorithm for a 1D array. I have read this post Peak finding algorithm. This is exactly what I am trying to do, there is discussion about time complexity but nothing of sort of pseudocode. The problem:

Given an array [a,b,c,d,e,f,g] where a to g are numbers, b is a peak if and only if a<=b and b>=c.

Example: given an array {1,2,3,4,5,19,25,20}, index 6 should be returned. The edge cases should give:

{100,4,3,1,19,20} -- index 0

{1,3,5,19,20} -- index 4

I have implemented in Java. my current run-time is O(n). I am wondering if this could be improved

public static int naive(int[] arr){
        int l=arr.length;
        if (arr[0]>=arr[1]) {
            return 0;
        }
        if (arr[l-1]>arr[l-2]){
            return l-1;
        }
        for (int i=1; i < arr.length-1;i++){
             if (arr[i] >= arr[i-1] && arr[i] >= arr[i+1] ){
                 return i;
             }
        }
        return -1;
    }
Community
  • 1
  • 1
eagertoLearn
  • 9,772
  • 23
  • 80
  • 122
  • 2
    could be coded using divide and conquer algorithm and that should give `O(log n)` – brain storm Feb 14 '14 at 20:03
  • 2
    @user1988876: how will that work, I need to go through entire array to find the peak. so the time complexity must be `O(n)` – eagertoLearn Feb 14 '14 at 20:04
  • 2
    you are interested in finding A single peak, not all peaks. – brain storm Feb 14 '14 at 20:05
  • Just to understand it better, if you have an array with values [5,5,5,7,2] you consider the second 5 to be a peak? – George Feb 14 '14 at 20:32
  • @George: the second one is the peak (because of >=) – eagertoLearn Feb 14 '14 at 20:33
  • But shouldn't 7 be the peak in this case? The 5's form a plateau. – George Feb 14 '14 at 20:35
  • @George: there are both I guess. 7 is global peak but 5 and 7 are local peaks – eagertoLearn Feb 14 '14 at 20:38
  • I edited the question to take this also into consideration. Although my first version does not consider those peaks, it will still return a valid peak as defined by you. I assume that you don't care which peak will actually be returned. The second version should give the same results as your implementation. – George Feb 14 '14 at 20:45
  • @George: +1 thanks for helping out. I will read through the solution carefully to understand – eagertoLearn Feb 14 '14 at 20:58

4 Answers4

3

The following function is probably a tiny bit more efficient than yours. Note that this will find a local maximum.

    public static int findLocalMaximum(int[] arr){
        int i = 0;
        while(i + 1 < arr.length && arr[i+1] >= arr[i]) {
            ++i;
        }
        return i;
    }

Edit: The following function finds peaks as defined in the question. Note that I removed the boundary check in the while loop, as the loop will only be reached if arr[l-2] > arr[l-1], an so the condition in the while loop will be false and l-2 will be returned.

    public static int findPeak(int[] arr){
        int l = arr.length;
        if(arr[0] >= arr[1]) {
            return 0;
        }

        if(arr[l-1] >= arr[l-2]) {
            return l-1;
        }

        int i = 1;
        while(arr[i+1] > arr[i]) {
            ++i;
        }
        return i;
    }
George
  • 3,765
  • 21
  • 25
2

If I may propose my solution:

public static int findPeak(int[] array, int start, int end) {
    int index = start + (end - start) / 2;

    if (index - 1 >= 0 && array[index] < array[index - 1]) {
        return findPeak(array, start, index - 1);
    } else if (index + 1 <= array.length - 1 && array[index] < array[index + 1]) {
        return findPeak(array, index + 1, end);
    } else {
        return array[index];
    }
}

I think it is simpler to handle edge cases directly in the if statements. I also tested with a couple of inputs and it seems to work.

Remy Cilia
  • 2,573
  • 1
  • 20
  • 31
1

A binary search-like algorithm should work. That employs divide and conquer strategy. since you are interested in single peak, the best you can do is O(log n). But if you want to find all peaks, it will be O(n) atleast.

Here is the divide-and conquer algorithm:

public static int peak1D(int[] arr, int start, int end){
          //edge cases
        if (end-start==1){
            if (start==0)
                return start;
            else 
             return end;
        }
        int i = start+end>>>1;
        if (arr[i]<arr[i-1])
           return peak1D(arr,start,i);
        if (arr[i]<arr[i+1]){
            return peak1D(arr, i, end);
        }
        else
            return i;
    }

I tested with a couple of inputs and it seems to work. my handling of edge cases is not great. though it is simple reasoning

brain storm
  • 30,124
  • 69
  • 225
  • 393
  • 1
    sorry, I am not convinced if that will work. or may be I am not understanding it clearly.can you give a pseudocode I could try? – eagertoLearn Feb 14 '14 at 20:09
  • @eagertoLearn: give me few minutes, I am working on it – brain storm Feb 14 '14 at 20:10
  • @Marichyasana: I meant `divide and conquer` when I said `binary search LIKE` – brain storm Feb 14 '14 at 20:19
  • 2
    @user1988876, try `{3, 2, 1, 0, 5, 4}`. Doesn't work. The problem is that there's no way to tell if a local peak exists or doesn't exist in the top or bottom half by looking at the middle. To find a local peak, you have to look through the entire array sequentially until you find one or hit the end. – gdejohn Feb 14 '14 at 20:26
  • @gdejohn: it works. the index is 0. which is the edge case. a local peak is one that if you hit you just return, a global peak is one and only one for entire array – brain storm Feb 14 '14 at 20:28
  • @gdejohn: There are two local peaks, the first one (which is index 0) and 5 (index 4). I want to find any one local peak. – eagertoLearn Feb 14 '14 at 20:36
  • @user1988876, my apologies, I misunderstood the question. My downvote is locked in, but I can retract it if you make an edit. – gdejohn Feb 14 '14 at 20:39
  • This algorithm will fail for `{ 1, 10, 3, 4, 5, 6, 0 }`. The index it returns will be 5 when the desired answer is 1. This algorithm will find *a* peak, but it won't find the first peak. That's not clear from the question, but comments by the poster seem to indicate that that's what they're interested in finding. – Kyle Feb 14 '14 at 20:54
  • @Kyle, both of those indices are local peaks, and the question is about finding any local peak. – gdejohn Feb 14 '14 at 20:55
  • 1
    this algorithm works because the edge-cases (arr[0], arr[n-1]) are allowed. – Bernd Elkemann Feb 14 '14 at 21:33
  • There is another version of this problem that excludes edge-cases but still works with this suggested algorithm: And that is when the array is unimodal meaning that there is an increasing sequence followed by a decreasing sequence and you are looking to find the unique local maximum element there. It would be O(log n) to find that. – Mahshid Zeinaly May 26 '14 at 03:38
0

Assume Array is global for simplicity you can pass it if you want.

    private static int getPeak1D(int start,int end)
{
    int x = (start+end)/2;

    if(     (x == 0 && array[x] >= array[x+1]) ||
            (x == array.length-1 && array[x]>=array[x-1]) ||
            (x>0 && x< array.length-1 && array[x] >= array[x-1] && array[x] >= array[x+1]))
        return x;

    if(x+1 < array.length && array[x] < array[x+1])
        temp =  getPeak1D(x+1,end);

    if(temp > -1)
        return temp;

    if(x-1 > -1 && array[x] < array[x-1])
        return getPeak1D(0,x-1);
    else
        return -1;
}

The First if check first edge, second edge, and inner part for peaks. if no peaks are found, if array[x+1] > array[x], The Second if will go to the right half of the array. we store it in temp (i have it as global) we dont return it because in case both array[x+1] and array[x-1] are both bigger than array[x] but the right side didn't find any peaks you should go to the third if to check the left side

Check this for the logic behind it https://courses.csail.mit.edu/6.006/spring11/rec/rec02.pdf

Ayman H
  • 27
  • 5