π Binary Search Patterns: The Ultimate Treasure Hunt
Imagine youβre looking for a hidden treasure in a row of 1000 numbered boxes. Would you open them one by one? Or would you be clever about it?
π― The Big Idea: Divide and Conquer
Binary Search is like playing a guessing game. Your friend thinks of a number between 1 and 100. Each time you guess, they say βhigherβ or βlower.β
The smart move? Always guess the middle. This cuts your possibilities in half every single time!
100 boxes β 50 β 25 β 12 β 6 β 3 β 1
Only 7 guesses to find anything!
π 1. Binary Search Fundamentals
The Core Recipe
Think of a sorted bookshelf. To find book #47:
- Look at the middle book
- If itβs #47 β Found it! π
- If #47 is smaller β look in the left half
- If #47 is bigger β look in the right half
- Repeat until found
def binary_search(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1 # Not found
Example: Finding 7
Array: [1, 3, 5, 7, 9, 11, 13]
Step 1: mid = 7 (index 3) β Found! π
Time Complexity: O(log n) β Lightning fast!
π 2. Binary Search Variants
πΉ Find First Occurrence
When duplicates exist, find the leftmost match.
Like finding the first person named βAlexβ in a sorted attendance list.
def first_occurrence(arr, target):
left, right = 0, len(arr) - 1
result = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
result = mid
right = mid - 1 # Keep looking left
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return result
πΉ Find Last Occurrence
Find the rightmost match instead.
def last_occurrence(arr, target):
left, right = 0, len(arr) - 1
result = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
result = mid
left = mid + 1 # Keep looking right
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return result
Example
Array: [1, 2, 2, 2, 3, 4]
Target: 2
First occurrence: index 1
Last occurrence: index 3
π 3. Search in Rotated Sorted Array
The Twist
Imagine a sorted array that got rotated β like spinning a deck of cards and cutting it mid-way.
Original: [1, 2, 3, 4, 5, 6, 7]
Rotated: [4, 5, 6, 7, 1, 2, 3]
β starts here now!
The Trick
At least one half is always sorted! Check which half is sorted, then decide where to look.
def search_rotated(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
# Left half is sorted
if arr[left] <= arr[mid]:
if arr[left] <= target < arr[mid]:
right = mid - 1
else:
left = mid + 1
# Right half is sorted
else:
if arr[mid] < target <= arr[right]:
left = mid + 1
else:
right = mid - 1
return -1
Example
Array: [4, 5, 6, 7, 0, 1, 2]
Find: 0
mid = 7 β Right half has 0
mid = 1 β Left half has 0
Found at index 4! π
π 4. Find Minimum in Rotated Sorted Array
The Challenge
Find the smallest element in a rotated array β itβs the pivot point!
[4, 5, 6, 7, 0, 1, 2]
β minimum!
The Pattern
The minimum is where the rotation happened. Look for where the order βbreaks.β
def find_min(arr):
left, right = 0, len(arr) - 1
while left < right:
mid = (left + right) // 2
if arr[mid] > arr[right]:
# Min is in right half
left = mid + 1
else:
# Min is in left half (including mid)
right = mid
return arr[left]
Example
Array: [3, 4, 5, 1, 2]
mid = 5 β 5 > 2 β go right
mid = 1 β 1 < 2 β go left
Answer: 1 π
π 5. Find Peak Element
Whatβs a Peak?
A peak is an element greater than its neighbors.
[1, 3, 7, 4, 2]
β peak!
The Insight
If youβre going uphill, a peak is ahead. If youβre going downhill, a peak is behind.
def find_peak(arr):
left, right = 0, len(arr) - 1
while left < right:
mid = (left + right) // 2
if arr[mid] < arr[mid + 1]:
# Going uphill, peak is ahead
left = mid + 1
else:
# Going downhill, peak is here or behind
right = mid
return left # Index of peak
Example
Array: [1, 2, 1, 3, 5, 6, 4]
mid = 3 β 3 < 5 β go right
mid = 6 β 6 > 4 β go left
Peak at index 5 (value 6) π
π 6. Binary Search on Answer
The Big Shift
Instead of searching in an array, we search for the best answer in a range of possibilities.
Think: βWhatβs the minimum capacity needed to ship packages in 5 days?β
The Pattern
1. Define possible answer range [min, max]
2. Check if mid value works (feasibility check)
3. If yes β try smaller (minimize) or larger (maximize)
4. If no β adjust range accordingly
Example: Square Root
Find β16 without using math.sqrt()
def sqrt(n):
left, right = 0, n
answer = 0
while left <= right:
mid = (left + right) // 2
if mid * mid <= n:
answer = mid
left = mid + 1
else:
right = mid - 1
return answer
# sqrt(16) = 4 π
π 7. Capacity to Ship Packages Pattern
The Problem
Ship packages within D days. Each package has a weight. Find the minimum ship capacity needed.
Packages: [1, 2, 3, 4, 5]
Days: 3
The Approach
- Min capacity = largest package (must fit!)
- Max capacity = sum of all packages (ship all in 1 day)
- Binary search for the minimum valid capacity
def ship_packages(weights, days):
def can_ship(capacity):
day_count = 1
current_load = 0
for w in weights:
if current_load + w > capacity:
day_count += 1
current_load = 0
current_load += w
return day_count <= days
left = max(weights)
right = sum(weights)
while left < right:
mid = (left + right) // 2
if can_ship(mid):
right = mid
else:
left = mid + 1
return left
Example
Packages: [1, 2, 3, 4, 5], Days: 3
Capacity 6: Day1:[1,2,3] Day2:[4] Day3:[5] β 3 days β
Capacity 5: Day1:[1,2] Day2:[3] Day3:[4] Day4:[5] β 4 days β
Answer: 6 π
π 8. Split Array Largest Sum
The Problem
Split an array into M parts. Minimize the largest sum among all parts.
Array: [7, 2, 5, 10, 8]
M: 2
Split: [7, 2, 5] and [10, 8]
Sums: 14 and 18
Largest: 18
The Insight
This is the inverse of the shipping problem! Weβre limiting the βloadβ (sum) per βshipβ (subarray).
def split_array(nums, m):
def can_split(max_sum):
parts = 1
current_sum = 0
for n in nums:
if current_sum + n > max_sum:
parts += 1
current_sum = 0
current_sum += n
return parts <= m
left = max(nums)
right = sum(nums)
while left < right:
mid = (left + right) // 2
if can_split(mid):
right = mid
else:
left = mid + 1
return left
Example
Array: [7, 2, 5, 10, 8], M: 2
max_sum = 18: [7,2,5] [10,8] β 2 parts β
max_sum = 17: [7,2,5] [10] [8] β 3 parts β
Answer: 18 π
πΊοΈ The Pattern Map
graph TD A["Binary Search Problem"] --> B{Sorted Array?} B -->|Yes| C{Normal or Rotated?} B -->|No - Search Answer Range| D["Binary Search on Answer"] C -->|Normal| E["Standard Binary Search"] C -->|Rotated| F{Find What?} F -->|Target Value| G["Search in Rotated Array"] F -->|Minimum Element| H["Find Minimum in Rotated"] E --> I{Duplicates?} I -->|Yes| J["First/Last Occurrence Variant"] I -->|No| K["Classic Binary Search"] D --> L["Capacity/Split Problems"] D --> M["Find Peak Element"]
π Key Takeaways
| Pattern | When to Use | Key Insight |
|---|---|---|
| Basic BS | Sorted array, find target | Halve the search space |
| Variants | Find first/last occurrence | Continue after finding |
| Rotated | Shifted sorted array | One half is always sorted |
| Find Min | Rotated array, find pivot | Look for the βbreakβ |
| Peak | Find local maximum | Follow the uphill direction |
| BS on Answer | Optimize min/max result | Search possible answers |
| Capacity | Distribute within limit | Binary search on capacity |
| Split Sum | Minimize maximum chunk | Same as capacity pattern! |
π Youβve Got This!
Binary search isnβt just about finding elements β itβs a thinking pattern. Whenever you see:
- Sorted data
- Min/max optimization
- βIs this value possible?β
Think Binary Search!
Keep practicing, keep halving, keep conquering! π
