Binary Search Foundations

7 concept walkthroughs, each with a worked explanation and an interactive visualization, before you start solving problems in this area.

What is Binary Search?

Binary Search is a search algorithm that finds the position of a target value within a SORTED collection. Instead of checking every element one by one (linear search), it repeatedly divides the search space in half. Each comparison eliminates half of the remaining elements, making it incredibly efficient: O(log n) vs O(n).

Basic Binary Search

The classic binary search: given a sorted array and a target, find its index. Start with left=0 and right=n-1. Compute mid = left + (right-left)/2. If nums[mid] == target, found it! If nums[mid] < target, search right half (left = mid+1). If nums[mid] > target, search left half (right = mid-1). Repeat until left > right.

def binary_search(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

Lower Bound (First Occurrence)

Sometimes the target appears multiple times and you need the FIRST occurrence (leftmost position). Or you need the first element >= target (lower bound). The trick: when you find the target, DON'T stop! Instead, save the position and keep searching LEFT (right = mid - 1) to see if there's an earlier occurrence.

def lower_bound(nums, target):
    left, right = 0, len(nums) - 1
    result = -1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] >= target:
            result = mid  # save and search left
            right = mid - 1
        else:
            left = mid + 1
    return result

Upper Bound (Last Occurrence)

The mirror of lower bound: find the LAST occurrence of the target (rightmost position). When you find the target, save the position and keep searching RIGHT (left = mid + 1). Combined with lower bound, you can find the range [first, last] of any target value.

def upper_bound(nums, target):
    left, right = 0, len(nums) - 1
    result = -1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] <= target:
            result = mid  # save and search right
            left = mid + 1
        else:
            right = mid - 1
    return result

Search in Rotated Array

A sorted array that's been rotated (e.g., [4,5,6,7,0,1,2]) is no longer fully sorted, but ONE HALF is always sorted! At each step, determine which half is sorted, then check if the target lies in that sorted half. If yes, search there. If no, search the other half.

def search_rotated(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        if nums[left] <= nums[mid]:  # left sorted
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        else:                        # right sorted
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1

Binary Search on Answer

The most powerful pattern! Instead of searching an array, you binary search on the ANSWER SPACE. If the answer must be between min_val and max_val, binary search that range. For each candidate answer (mid), check if it's feasible using a helper function. This transforms optimization problems into binary search!

def binary_search_on_answer(low, high):
    result = high
    while low <= high:
        mid = low + (high - low) // 2
        if canAchieve(mid):
            result = mid   # save and try smaller
            high = mid - 1
        else:
            low = mid + 1
    return result

Patterns & Pitfalls

Binary search has several common patterns and pitfalls. The three main templates are: (1) Exact match. Return when found, (2) Left boundary. Keep going left after finding, (3) Right boundary, keep going right after finding. Common bugs include off-by-one errors, infinite loops from wrong mid calculation, and forgetting the sorted prerequisite.

All DSA learning paths