🌍
Why does this topic matter?
Searching is how every large system finds things fast. Google indexes billions of pages and returns results in milliseconds because of binary search principles at every layer. Binary Search turns an O(N) problem into O(log N) — for N = 1 billion, that's the difference between 1 billion operations and just 30. Beyond simple element search, "Binary Search on Answer Space" is one of the most powerful interview tricks — it converts problems that look impossible into clean O(N log N) solutions.

📖 Before We Start — The Big Picture

Searching is the problem of finding a target element in a dataset. The strategy depends entirely on what you know about the data:

The key insight of Binary Search is the invariant: at every step, you maintain the guarantee that the target — if it exists — lies within [lo, hi]. By checking the midpoint, you eliminate half the remaining candidates. You only need 30 checks to find any element in a 1-billion element array.

🔗
How it connects: Lecture 9 (Sorting) is the prerequisite — Binary Search requires sorted data. Lecture 5 (Recursion) shows the elegant recursive formulation of Binary Search. Binary Search on answer space appears in later lectures on Graphs (finding minimum cost) and Dynamic Programming.

🌐 Search Overview

The goal of searching algorithms is simply to locate a target element in a dataset. Optimization comes from leveraging the properties of the dataset:

  • Linear Search: The universal fallback. Scans one by one. Required when data is unsorted.
  • Binary Search: The divide-and-conquer king. Slices domains in half, but requires sorted data.
  • Ternary Search: Divides space into thirds. Specialized for finding peaks/valleys in unimodal functions.
  • Interpolation Search: Guesses the location based on values. Incredible speed, but requires uniformly distributed data.

🔬 Algorithms Deep Dive

Linear Search

While mathematically $O(N)$ is slow, it is paradoxically the fastest search for tiny arrays ($N < 100$) due to CPU contiguous memory caching avoiding pointer jumping overhead.

Caveat - The Recursion Trap: Writing Linear Search recursively occupies $O(N)$ Space due to the Call Stack. Always write Linear Search iteratively in Java!

☕ Java · Linear Search (Iterative vs Recursive)
// Iterative: Space O(1)
public int linearSearch(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] == target) return i;
    }
    return -1;
}

// Recursive: Space O(N) due to Call Stack
public int recursiveLinear(int[] nums, int target, int index) {
    if (index >= nums.length) return -1;
    if (nums[index] == target) return index;
    return recursiveLinear(nums, target, index + 1);
}

Binary Search

Systematically eliminates halves. Heavily vulnerable to infinite loops if boundaries (`low`, `high`) overlap incorrectly.

Caveat - The Overflow Bug: Calculating mid = (low + high) / 2 overflows the 32-bit integer limit if boundaries are massive. Always use: mid = low + (high - low) / 2.

☕ Java · Classic Binary Search
public int binarySearch(int[] nums, int target) {
    int lo = 0, hi = nums.length - 1;
    while (lo <= hi) { // '<=' allows exhaustive 1-element checks
        int mid = lo + (hi - lo) / 2;
        if (nums[mid] == target) return mid;
        if (nums[mid] < target) lo = mid + 1;
        else hi = mid - 1;
    }
    return -1;
}

Interpolation Search

Instead of blindly checking the middle, it estimates position: pos = lo + ((target - arr[lo]) * (hi - lo) / (arr[hi] - arr[lo])).

Good to Know: Outperforms Binary Search by reaching $O(\log(\log N))$, but only if data is linearly uniform (e.g., 10, 20, 30...). If clumped heavily, it crashes to $O(N)$.

☕ Java · Interpolation Search
while (lo <= hi && target >= arr[lo] && target <= arr[hi]) {
    // Prevent division by zero
    if (lo == hi) {
        if (arr[lo] == target) return lo;
        return -1;
    }
    int pos = lo + (((hi - lo) / (arr[hi] - arr[lo])) * (target - arr[lo]));
    if (arr[pos] == target) return pos;
    if (arr[pos] < target) lo = pos + 1;
    else hi = pos - 1;
}

Ternary Search

Calculates mid1 and mid2 to eliminate a third of the array per pass. It is mathematically slower than Binary Search (`2 * log_3(N) > log_2(N)`), so it is never used for exact targeting natively.

Primary Use Case: Finding the maximum/minimum point in a continuous Unimodal function (a curve that goes strictly up, then strictly down).

☕ Java · Ternary Search (Peak Finding)
while (hi - lo > 2) {
    int mid1 = lo + (hi - lo) / 3;
    int mid2 = hi - (hi - lo) / 3;
    // Assuming we are searching for the Maximum peak in f(x)
    if (f(mid1) < f(mid2)) lo = mid1; // Max must be right of mid1
    else hi = mid2; // Max must be left of mid2
}

🔄 Algorithm Variations

Upper / Lower Bounds (Duplicates)

If finding the first occurrence of a duplicate, when `nums[mid] == target`, don't stop! Keep searching left: ans = mid; high = mid - 1; to capture the earliest index boundary.

☕ Java · Lower Bound logic
int ans = -1;
while (lo <= hi) {
    int mid = lo + (hi - lo) / 2;
    if (nums[mid] == target) {
        ans = mid; // Capture possible target
        hi = mid - 1; // Keep looking left for earlier occurrence
    } else if (nums[mid] < target) lo = mid + 1;
    else hi = mid - 1;
}

Search in Rotated Sorted Array

If an array is shifted (`[4, 5, 6, 7, 0, 1, 2]`), you test which half is strictly monotonic. If `nums[lo] <= nums[mid]`, the left side is perfectly sorted, and you can evaluate containment safely.

☕ Java · Sorted Half Validation
if (nums[lo] <= nums[mid]) { // Left half is perfectly sorted
    if (target >= nums[lo] && target < nums[mid]) hi = mid - 1; // Must be in left
    else lo = mid + 1; // Must be in right
} else { // Right half is perfectly sorted
    if (target > nums[mid] && target <= nums[hi]) lo = mid + 1; // Must be in right
    else hi = mid - 1; // Must be in left
}

Binary Search on Answer Space

Searching for an abstract minimum/maximum configuration instead of an array index.

  1. Domain: Establish `lo` = absolute min answer, `hi` = absolute max.
  2. isValid(mid): Write a secondary $O(N)$ function to test if `mid` properly satisfies all constraints.
  3. Optimize: If `isValid(mid)` is logically valid, try optimizing further by lowering requirements: `hi = mid`. Otherwise, `lo = mid + 1`.
☕ Java · Answer Space Template
while (lo < hi) {
    int mid = lo + (hi - lo) / 2;
    if (isValid(mid)) {
        hi = mid; // It works! See if we can find a smaller valid capacity
    } else {
        lo = mid + 1; // Did not work. We must increase capacity
    }
}
return lo;

⏱️ Complexity Analysis

A global snapshot verifying Time bounds and internal memory Space usage natively.

Algorithm Condition Time (Worst) Time (Best) Space
Linear Search Unsorted $O(N)$ $O(1)$ $O(1)$
Binary Search Sorted $O(\log_2 N)$ $O(1)$ $O(1)$
Ternary Search Sorted / Unimodal $O(\log_3 N)$ $O(1)$ $O(1)$
Interpolation Sorted, Uniform $O(N)$ $O(\log(\log N))$ $O(1)$

🗺️ 2D Matrix Traversals

Perfectly Flattened Binary Search

If a matrix `M x C` is strictly sorted universally (the end of Row 1 is less than the start of Row 2), treat it like a 1D array of length `M * C`.

The Indexing Math:
Row Index = mid / C
Col Index = mid % C

☕ Java · 1D to 2D Mapping
int rows = matrix.length, cols = matrix[0].length;
int lo = 0, hi = (rows * cols) - 1;
while (lo <= hi) {
    int mid = lo + (hi - lo) / 2;
    int val = matrix[mid / cols][mid % cols];
    if (val == target) return true;
    if (val < target) lo = mid + 1;
    else hi = mid - 1;
}

Staircase / Saddleback Search

If the array is only sorted horizontally per row and vertically per col (meaning Row 2 can start with a smaller number than the end of Row 1), you cannot flatten it. You start exclusively at the Top-Right coordinate, shrinking the domain leftwards or downwards in $O(M + N)$ combinations.

☕ Java · Top-Right Staircase
int row = 0, col = matrix[0].length - 1;
while (row < matrix.length && col >= 0) {
    if (matrix[row][col] == target) return true;
    if (matrix[row][col] > target) col--; // Eliminate current column
    else row++; // Eliminate current row
}

💪 Practice Problems

Problem 01 · Rotated Shift
Search in Rotated Sorted Array
Medium Google Microsoft
Problem 02 · Unsorted Array Bounds
Find Peak Element
Medium Uber Amazon
Problem 03 · Dual Partition Arrays
Median of Two Sorted Arrays
Hard Apple Adobe
Problem 04 · Answer Space bounds
Koko Eating Bananas
Medium Airbnb
Problem 05 · Grid Flattening
Search a 2D Matrix
Medium Amazon
Problem 06 · Find Pivot
Minimum in Rotated Sorted Array
Medium MicrosoftAmazon Binary Search
Problem 07 · Lower/Upper Bound
Find First and Last Position of Element
Medium FacebookGoogle Binary Search Bounds

📝 Topic Assignment

📋
Topic 10 Assignment — 28 Problems
Complete the assignment before moving forward. It includes 8 Easy, 13 Medium, 7 Hard problems, and conceptual checks — all carefully chosen to master Searching algorithms.

📄 Open Assignment →

✅ Topic Completion Checklist

Check each item before advancing to Topic 11.

I can distinguish when to use Linear Search over Binary Search
I understand why `mid = low + (high - low) / 2` prevents integer overflow
I can implement standard Binary Search in under 2 minutes bug-free
I know the 3 main Binary Search templates and their boundary conditions
I can map a 1D index to a 2D matrix safely via `[mid/nc][mid%nc]`
I understand the monotonic property required to pivot Rotated Arrays
I can solve 'Search in Rotated Sorted Array' efficiently
I can formulate `isValid(mid)` checks for Answer Space patterns
I understand the dual partition formula for Median of Two Arrays
I have completed successfully at least 20 problems from the Assignment
🧠
You're ready for Topic 11: Linked Lists
We're stepping out of contiguous arrays and stepping into dynamic node allocation! Master pointer tracking now because it will be absolutely vital for Trees and Graphs.