Search In Mountain Array

Monotonic arrays are strictly sorted in ascending or descending order, but in Bitonic or Mountain arrays, some elements are in ascending order and then descending order. for example
0, 1, 0 here 1 is the peak element, and all elements before 1 are in ascending order, and after 1 is in descending order.
1, 2, 3, 4, 5, 4, 3, 2, 1 here 5 is the peak element, and all elements before 5 are in ascending order, and after 5 are in descending order.

The Algorithm is simple

  • Find the Peak element in the Mountain array
  • Search for the element in the first half of the Mountain array, ie from the start of the array up to the index of the Peak element.
  • If the target element is found in the first half of the array, then it's the result, else function will return -1.
  • else search for the element in the second half of the Mountain array, ie from the index of the Peak element up to the end of the array.
package org.wesome.dsalgo;

public class SearchInMountainArray {
    public static int orderAgnosticBinarySearch(int[] arr, int find, int start, int end) {
//        find the sort order of the array
        boolean isAsc = arr[start] < arr[end];

        while (start <= end) {
            int mid = start + (end - start) / 2;
            // If the target element is present in the middle itself
            if (arr[mid] == find) {
                return mid;
            }
//            check the sort order of the array
            if (isAsc) {
                // In ascending order sorted array, If the find is smaller than mid-element, then it can only be present in left sub-array
                if (arr[mid] > find) {
                    end = mid - 1;
                }
                // Else the element can only be present in right sub-array
                else {
                    start = mid + 1;
                }
            } else {
                // In descending order sorted array, If the element is smaller than mid-element, then it can only be present in right sub-array
                if (arr[mid] < find) {
                    end = mid - 1;
                }
                // Else the element can only be present in left sub-array
                else {
                    start = mid + 1;
                }
            }
        }
        return -1;
    }

    public static int peakInMountainArray(int[] arr) {
        int start = 0, end = arr.length - 1;
        while (start < end) {
            int mid = start + (end - start) / 2;
//            if the middle element is greater than its next element, it means its decreasing part of the array
            if (arr[mid] > arr[mid + 1]) {
//                mid might be the peak of the array, but there is a possibility that an even higher peak may lie on the left sub-array. so keep the mid also, that's why the end is not equal to mid-1
                end = mid;
            }
//            else middle element is less than its next element, it means its increasing part of the array
            else {
//                in the above if condition, its already checked that the mid-element is greater than mid+1, so we don't need to keep mid anymore, hence start will be mid+1
                start = mid + 1;
            }
        }
//        in the end, the start index and end index both will point to the largest number
        return start;
    }

    public static int searchInMountainArray(int[] arr, int find) {
//        Find the peak element in the mountain array
        int peakInMountainArray = peakInMountainArray(arr);
//        find the target element in the first half of the array, ie from the start index up to the index of the peak element
        int findInAscendingOrder = orderAgnosticBinarySearch(arr, find, 0, peakInMountainArray);
//        if the element is found in the first half, it the result, else search in the second half of the array, ie from the index of the peak element up to the end of the array
        if (findInAscendingOrder != -1) {
            return findInAscendingOrder;
        } else {
            return orderAgnosticBinarySearch(arr, find, peakInMountainArray + 1, arr.length - 1);
        }
    }
}
package org.wesome.dsalgo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.wesome.dsalgo.SearchInMountainArray.searchInMountainArray;

public class SearchInMountainArrayTest {
    @Test
    void searchIMountainArrayTest1() {
        int[] arr = new int[]{0, 1, 0, 2, 0, 4, 0, 6, 0};
        int searchIMountainArray = searchInMountainArray(arr, 6);
        Assertions.assertEquals(7, searchIMountainArray);
    }

    @Test
    void searchIMountainArrayTest2() {
        int[] arr = new int[]{1, 2, 3, 4, 5, 4, 3, 2, 1, 0};
        int searchIMountainArray = searchInMountainArray(arr, 0);
        Assertions.assertEquals(9, searchIMountainArray);
    }

    @Test
    void searchIMountainArrayTest3() {
        int[] arr = new int[]{1, 3, 5, 7, 6, 4, 2, 0};
        int searchIMountainArray = searchInMountainArray(arr, 1);
        Assertions.assertEquals(0, searchIMountainArray);
    }

    @Test
    void searchIMountainArrayTest4() {
        int[] arr = new int[]{1, 2, 1, 3, 5, 6, 4};
        int searchIMountainArray = searchInMountainArray(arr, 7);
        Assertions.assertEquals(-1, searchIMountainArray);
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.4.1"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

 

follow us on