Back to Arrays
Q79Question 79 of 109

Search Bitonic Array (Find a Key in a Rise-Then-Fall Array)

Key Concepts
  • Array traversal, search, and manipulation
  • Common patterns: two-pointer, prefix sum, sliding window
  • Find max/min, reverse, rotate, remove duplicates
  • Time complexity: O(n) traversal, O(1) access by index
  • We first find the peak index with the Bitonic Maximum technique, comparing each middle with its right neighbour.
  • Once we have `peak`, we run a standard ascending binary search over indices `0.
  • If it returns a valid index we return it, because the left side holds the smaller indices.

Definition

Find the peak, binary-search the ascending left half, then if needed binary-search the descending right half, returning the earlier match.

Search Bitonic Array combines two Modified Binary Search ideas. First we find the peak index of the bitonic array. That peak splits the array into an ascending part on the left and a descending part on the right. We binary-search the ascending part normally, and if the key is not there we binary-search the descending part with reversed comparisons. Returning the first match gives the smaller index when the key appears twice.

At the National Bank of India, the intraday price series of Rajesh's stock rises to a high then falls, and the compliance team asks at which point in the day the price first hit a specific value. We find the daily high, then search the rising half, and if not found there, search the falling half.

Understanding the Concepts

What is the problem?

Search Bitonic Array (Find a Key in a Rise-Then-Fall Array). Given a bitonic array, strictly increasing then strictly decreasing, find the index of a given key. If the key appears on both sides, return the smaller index. Explain how you first find the peak and then binary-search each side with the correct order.

How does the approach work?

We first find the peak index with the Bitonic Maximum technique, comparing each middle with its right neighbour. Once we have peak, we run a standard ascending binary search over indices 0..peak. If it returns a valid index we return it, because the left side holds the smaller indices. Otherwise we run a descending binary search over indices peak + 1..n - 1, where the comparisons are reversed: key < arr[mid] moves start = mid + 1 and key > arr[mid] moves end = mid - 1. For example, the descending search uses if (key == arr[mid]) return mid; else if (key < arr[mid]) start = mid + 1; else end = mid - 1;.

Returning the left-side match first naturally yields the smaller index when the key exists on both the rising and falling sides. Each of the three phases, finding the peak and the two searches, is logarithmic, so the whole algorithm is logarithmic.

A senior nuance is that the descending search is just an ordinary binary search with the inequality signs flipped, so reusing a single order-aware search routine keeps the code clean. Another nuance is correctly partitioning at the peak so neither search overlaps the other. Complexity is O(log n) time and O(1) space.

What is the banking use case?

Problem 1 — The National Bank of India stores the intraday price of Rajesh's stock as a bitonic series, and compliance asks at which earliest point the price reached a specific value. This is exactly how Search Bitonic Array works: locate the key in a rise-then-fall array, preferring the earlier occurrence.
Problem 2 — A linear scan is too slow for long series, and a naive binary search fails because the array is not monotonic. This is why we first find the peak to split the series into two monotonic halves and binary-search each with the correct order.

The code defines a BitonicSearchService exposing firstIndexOf(prices, key) returning the earliest index or minus one. A Demo searches a bitonic price series for a value present on both sides and logs the earlier index.

How do you implement this in Java?

JAVA
// ─── File: src/main/java/com/bank/bitonic/service/BitonicSearchService.java ───
package com.bank.bitonic.service;

import java.util.logging.Logger;

// Searches a bitonic array: find peak, then search each monotonic side.
public class BitonicSearchService {
    private static final Logger log = Logger.getLogger(BitonicSearchService.class.getName());

    public int firstIndexOf(int[] prices, int key) {
        int peak = findPeak(prices);
        int left = search(prices, key, 0, peak, true);    // ascending side
        if (left != -1) {
            log.info(() -> "Key " + key + " found on rising side at " + left);
            return left;                                   // smaller index wins
        }
        int right = search(prices, key, peak + 1, prices.length - 1, false); // descending side
        log.info(() -> "Key " + key + " search returned index " + right + " on falling side");
        return right;
    }

    private int findPeak(int[] a) {
        int start = 0, end = a.length - 1;
        while (start < end) {
            int mid = start + (end - start) / 2;
            if (a[mid] > a[mid + 1]) end = mid; else start = mid + 1;
        }
        return start;
    }

    private int search(int[] a, int key, int start, int end, boolean ascending) {
        while (start <= end) {
            int mid = start + (end - start) / 2;
            if (a[mid] == key) return mid;
            if (ascending) {
                if (key < a[mid]) end = mid - 1; else start = mid + 1;
            } else {
                if (key < a[mid]) start = mid + 1; else end = mid - 1; // reversed
            }
        }
        return -1;
    }
}
JAVA
// ─── File: src/main/java/com/bank/bitonic/BitonicSearchApplication.java ───
package com.bank.bitonic;

import com.bank.bitonic.service.BitonicSearchService;
import java.util.logging.Logger;

public class BitonicSearchApplication {
    private static final Logger log = Logger.getLogger(BitonicSearchApplication.class.getName());

    public static void main(String[] args) {
        BitonicSearchService service = new BitonicSearchService();
        int[] prices = {101, 119, 140, 135, 119, 104};
        log.info("Earliest index of price 119: " + service.firstIndexOf(prices, 119));
    }
}

What is the project structure?

DIAGRAM
bitonic-search-service/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/com/bank/bitonic/
│   │   │   ├── BitonicSearchApplication.java     (main class)
│   │   │   └── service/
│   │   │       └── BitonicSearchService.java
│   │   └── resources/
│   │       └── application.yml
│   └── test/
│       └── java/com/bank/bitonic/
│           └── BitonicSearchServiceTest.java

How do you build and run?

BASH
# Build
mvn clean package -DskipTests

# Run
java -jar target/bitonic-search-service-1.0.0.jar

# See peak and side searches at FINE level
java -Djava.util.logging.ConsoleHandler.level=FINE \
  -jar target/bitonic-search-service-1.0.0.jar

What is the step-by-step walkthrough?

Input: prices 101, 119, 140, 135, 119, 104 and key 119.

Step 1: Find the peak. Comparisons converge to index 2, value 140, the high.
Step 2: Ascending search over indices 0 to 2. mid is 1, price at 1 is 119 which equals the key, so we return index 1 immediately.
Step 3: Because the rising side matched, we never search the falling side, so the earlier index 1 is returned even though 119 also appears at index 4.
CODE
Oct 27, 2026 10:11:55 PM com.bank.bitonic.service.BitonicSearchService firstIndexOf
INFO: Key 119 found on rising side at 1
INFO: Earliest index of price 119: 1

What is the high-level design?

DIAGRAM
  ═══ HLD — Compliance Price-Point Lookup (Banking) ═══

  ┌────────────┐ HTTPS ┌──────────────┐ REST ┌─────────────────────┐
  │ Compliance │──────→│ API Gateway  │─────→│ Bitonic Search ×2   │
  └────────────┘       │ (JWT, rate)  │      │ (Spring Boot)       │
                       └──────────────┘      └─────────┬───────────┘
                                                      │ load price series
                                                      ▼
                                           ┌─────────────────────┐
                                           │ Time-series store    │
                                           └─────────────────────┘

What is the low-level design?

DIAGRAM
  ═══ LLD — Search Bitonic Array ═══

  ┌─ Design Patterns Used ──────────────────────────────────┐
  │  Peak split + two order-aware binary searches            │
  │     WHY: each half is monotonic and searchable           │
  └──────────────────────────────────────────────────────────┘

  ┌─ SOLID Principles Applied ──────────────────────────────┐
  │  S: findPeak and search each do one job                  │
  │  O: boolean bias reuses one search for both orders       │
  │  D: depends only on primitive int array                  │
  └──────────────────────────────────────────────────────────┘

  ── Class Diagram ──────────────────────────────────────────
  ┌──────────────────────────────────────┐
  │ BitonicSearchService                  │
  │ ───────────────────────────────────── │
  │ + firstIndexOf(prices, key): int      │
  │ - findPeak(a): int                    │
  │ - search(a,key,s,e,ascending): int    │
  └──────────────────────────────────────┘

  ── State: peak index partitions the array ────────────────
  left [0..peak] ascending, right [peak+1..n-1] descending

  Contract for firstIndexOf(prices, key):
    Pre:  prices bitonic
    Post: earliest index of key, or -1

What is the time and space complexity?

Analyze the solution based on the algorithm used. Consider both the time to process input and the space required for any data structures.

What are common mistakes?

Common mistakes include off-by-one errors, not handling edge cases like empty input, and not considering the constraints of the problem.

What are the edge cases?

Edge cases include empty input, single element input, all identical elements, and boundary values.

What should a senior engineer know?

A senior engineer should understand the time-space tradeoffs, be able to explain the algorithm clearly, and know when this technique applies versus alternatives.

Visual Overview

DIAGRAM
  prices:[101,119,140,135,119,104], key=119
    │
    ▼
  findPeak → index 2 (140)
    │
    ├─ ascending search [0..2] → hit at index 1 ──→ return 1
    └─ (descending search [3..5] skipped; left won)
    ▼
  earliest index = 1

Key Takeaway

Search Bitonic Array is a composite Modified Binary Search problem. Because the array first rises and then falls, it is not globally monotonic, so a single binary search fails. My first move is to find the peak index using the slope comparison, where I compare each middle element with its right neighbour. That peak splits the array into a clean ascending half on the left and a clean descending half on the right.

Then I binary-search the ascending half with the usual comparisons, and if the key is not there I binary-search the descending half with the inequality signs reversed. I search the rising half first on purpose, because when the key appears on both sides the rising half holds the smaller index, so returning its match first gives the earliest occurrence. I keep both searches in one order-aware routine driven by a boolean so I am not duplicating logic.

At the National Bank of India I used this against the intraday price series of Rajesh's stock, stored as a bitonic series, when compliance asked at which earliest point in the day the price reached a specific value. I found the daily high to split the series, searched the rising half, and only searched the falling half if needed, returning the earlier index, all in logarithmic time. Explaining the peak split, the reversed comparisons on the descending side, and the left-first rule for the smaller index is what shows an interviewer I can compose two binary-search variants correctly.

Remember one sentence: understand the core algorithm and apply it to banking domain problems.

---

Share
Use ← → arrow keys to navigate