name: Binary Search
slug: binary-search
difficulty_level: 2
pattern_type: algorithm
display_order: 4

description: >
  Efficiently search sorted data by repeatedly dividing the search space in half.
  This transforms O(n) linear search into O(log n) by eliminating half the
  remaining possibilities with each comparison.

when_to_use: |
  - Sorted arrays or search spaces
  - Finding boundaries (first/last occurrence)
  - Searching in rotated sorted arrays
  - Finding peak elements
  - Minimizing/maximizing with monotonic constraints

metaphor: |
  Imagine playing a number guessing game where someone says "higher" or "lower"
  after each guess. The optimal strategy is always guessing the middle—you
  eliminate half the possibilities each time regardless of the answer.

  Another analogy: looking up a word in a physical dictionary. You don't read
  page by page from the start. You open roughly to the middle, see if you're
  before or after your word, then repeat in the appropriate half.

core_concept: |
  Binary search works because the data has **monotonic ordering**—if you find
  something too small, everything before it is also too small. If something is
  too big, everything after is also too big.

  The key insight extends beyond simple arrays:

  1. **Value search**: Find a specific target in sorted array
  2. **Boundary search**: Find the first/last element satisfying a condition
  3. **Search space**: Binary search over answers (e.g., "minimum capacity")

  At each step, you make one comparison and eliminate half the space. After k
  comparisons, you've narrowed n elements down to n/2^k. Solving n/2^k = 1
  gives k = log₂(n).

visualization: |
  **Example: Find target = 7 in sorted array**

  ```
  Array: [1, 3, 5, 7, 9, 11, 13]
          L        M          R

  Step 1: mid = 7, target = 7 → Found!
  ```

  **Example: Find target = 9**

  ```
  [1, 3, 5, 7, 9, 11, 13]
   L        M          R

  Step 1: mid = 7 < 9 → Search right half
                   L  M   R

  Step 2: mid = 11 > 9 → Search left half
                   L
                   M
                   R

  Step 3: mid = 9 → Found at index 4!
  ```

  **Binary search on answer: Minimum capacity to ship packages in D days**

  ```
  Search space: [max(weights), sum(weights)]

  mid = some capacity
  Can ship in D days with capacity mid?
    Yes → try smaller capacity (go left)
    No  → need more capacity (go right)
  ```

code_template: |
  def binary_search(arr: list, target: int) -> int:
      """Classic binary search for exact match."""
      left, right = 0, len(arr) - 1

      while left <= right:
          mid = left + (right - left) // 2  # Avoid overflow

          if arr[mid] == target:
              return mid
          elif arr[mid] < target:
              left = mid + 1
          else:
              right = mid - 1

      return -1  # Not found


  def lower_bound(arr: list, target: int) -> int:
      """Find first position where arr[i] >= target."""
      left, right = 0, len(arr)

      while left < right:
          mid = left + (right - left) // 2

          if arr[mid] < target:
              left = mid + 1
          else:
              right = mid

      return left  # First valid position


  def binary_search_answer(low: int, high: int, is_valid) -> int:
      """Binary search on answer space."""
      while low < high:
          mid = low + (high - low) // 2

          if is_valid(mid):
              high = mid  # Try smaller
          else:
              low = mid + 1  # Need bigger

      return low

recognition_signals:
  - "sorted array"
  - "O(log n)"
  - "find minimum/maximum"
  - "find first/last occurrence"
  - "rotated sorted array"
  - "peak element"
  - "minimum capacity"
  - "search space"
  - "lower bound"
  - "upper bound"

common_mistakes:
  - title: Integer overflow in mid calculation
    description: |
      Using `(left + right) / 2` can overflow if left and right are both
      large positive integers (in languages with fixed-size integers).
    fix: |
      Use `left + (right - left) // 2` instead. This is mathematically
      equivalent but avoids overflow.

  - title: Infinite loop with wrong boundary update
    description: |
      Using `right = mid` with `left <= right` condition, or `left = mid`
      when mid could equal left, causes infinite loops.
    fix: |
      For `left <= right`, always use `left = mid + 1` and `right = mid - 1`.
      For `left < right`, use `left = mid + 1` and `right = mid`.

  - title: Off-by-one with boundary search
    description: |
      Returning `left` when you should return `left - 1` (or vice versa)
      gives the wrong boundary element.
    fix: |
      Think carefully about loop invariants. What does `left` represent when
      the loop ends? Test with edge cases.

  - title: Not recognizing binary search applicability
    description: |
      Missing that a problem can use binary search because the "array" is
      implicit (search space of possible answers).
    fix: |
      If you need to minimize/maximize something and can write a function
      `is_valid(x)` that's monotonic, binary search applies.

variations:
  - name: Classic search
    description: |
      Find exact target in sorted array. Returns index or -1.
    example: "Binary Search, Search Insert Position"

  - name: Lower/Upper bound
    description: |
      Find first or last position satisfying a condition. Used for ranges
      and counting occurrences.
    example: "First Bad Version, Find First and Last Position"

  - name: Rotated array
    description: |
      Sorted array rotated at some pivot. One half is always sorted—determine
      which half and search appropriately.
    example: "Search in Rotated Sorted Array, Find Minimum"

  - name: Binary search on answer
    description: |
      Search the space of possible answers. Need a monotonic predicate function
      to determine feasibility.
    example: "Capacity To Ship Packages, Koko Eating Bananas, Split Array Largest Sum"

  - name: Peak finding
    description: |
      Find local maximum in bitonic array. Compare mid with neighbors to
      determine which side has the peak.
    example: "Find Peak Element, Find in Mountain Array"

related_patterns:
  - two-pointers

prerequisite_patterns: []

visualization_examples:
  - id: binary-search-find-target
    title: Find Target in Sorted Array
    input:
      nums: [1, 3, 5, 7, 9, 11, 13]
      target: 9
    code: |
      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
    steps:
      - id: step-1
        description: "Initialize pointers: left=0, right=6. Search space is entire array."
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: default }
              - { value: 3, state: default }
              - { value: 5, state: default }
              - { value: 7, state: default }
              - { value: 9, state: default }
              - { value: 11, state: default }
              - { value: 13, state: default }
            pointers:
              left: 0
              right: 6
        variables:
          target: 9
          left: 0
          right: 6
        codeHighlight:
          startLine: 2
          endLine: 2

      - id: step-2
        description: "Calculate mid=3. nums[3]=7 < target=9, so search right half."
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: visited }
              - { value: 3, state: visited }
              - { value: 5, state: visited }
              - { value: 7, state: comparing }
              - { value: 9, state: default }
              - { value: 11, state: default }
              - { value: 13, state: default }
            pointers:
              left: 0
              mid: 3
              right: 6
        variables:
          target: 9
          left: 0
          right: 6
          mid: 3
        codeHighlight:
          startLine: 9
          endLine: 10

      - id: step-3
        description: "Update left=4. New search space is indices 4-6."
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: visited }
              - { value: 3, state: visited }
              - { value: 5, state: visited }
              - { value: 7, state: visited }
              - { value: 9, state: default }
              - { value: 11, state: default }
              - { value: 13, state: default }
            pointers:
              left: 4
              right: 6
        variables:
          target: 9
          left: 4
          right: 6
        codeHighlight:
          startLine: 4
          endLine: 4

      - id: step-4
        description: "Calculate mid=5. nums[5]=11 > target=9, so search left half."
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: visited }
              - { value: 3, state: visited }
              - { value: 5, state: visited }
              - { value: 7, state: visited }
              - { value: 9, state: default }
              - { value: 11, state: comparing }
              - { value: 13, state: visited }
            pointers:
              left: 4
              mid: 5
              right: 6
        variables:
          target: 9
          left: 4
          right: 6
          mid: 5
        codeHighlight:
          startLine: 11
          endLine: 12

      - id: step-5
        description: "Update right=4. Search space narrowed to index 4 only."
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: visited }
              - { value: 3, state: visited }
              - { value: 5, state: visited }
              - { value: 7, state: visited }
              - { value: 9, state: active }
              - { value: 11, state: visited }
              - { value: 13, state: visited }
            pointers:
              left: 4
              right: 4
        variables:
          target: 9
          left: 4
          right: 4
        codeHighlight:
          startLine: 4
          endLine: 4

      - id: step-6
        description: "Calculate mid=4. nums[4]=9 == target=9. Found at index 4!"
        structures:
          nums:
            type: array
            values:
              - { value: 1, state: visited }
              - { value: 3, state: visited }
              - { value: 5, state: visited }
              - { value: 7, state: visited }
              - { value: 9, state: found }
              - { value: 11, state: visited }
              - { value: 13, state: visited }
            pointers:
              mid: 4
        variables:
          target: 9
          mid: 4
          result: 4
        codeHighlight:
          startLine: 7
          endLine: 8
