function binarySearch(nums, target) { let left = 0; let right = nums.length - 1; while (left <= right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) { return mid; } else if (nums[mid] < target) { left = mid + 1; // Search right half } else { right = mid - 1; // Search left half } } return -1; // Target not found}// Time: O(log n), Space: O(1)
Example: Recursive binary search
function binarySearchRecursive(nums, target, left = 0, right = nums.length - 1) { if (left > right) return -1; const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) return mid; if (nums[mid] < target) { return binarySearchRecursive(nums, target, mid + 1, right); } else { return binarySearchRecursive(nums, target, left, mid - 1); }}// Space: O(log n) due to recursion stack
2. Lower Bound and Upper Bound Implementation
Bound Type
Definition
Template
Result
Lower Bound
First index where arr[i] >= target
while (left < right); if (arr[mid] < target) left = mid+1
Leftmost position to insert target
Upper Bound
First index where arr[i] > target
while (left < right); if (arr[mid] <= target) left = mid+1
Rightmost position to insert target
First Occurrence
Leftmost index of target value
Lower bound with equality check
Start of range of equal values
Last Occurrence
Rightmost index of target value
Upper bound - 1 with equality check
End of range of equal values
Example: Lower bound (first >= target)
function lowerBound(nums, target) { let left = 0; let right = nums.length; while (left < right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] < target) { left = mid + 1; } else { right = mid; // Could be answer } } return left;}// [1,2,4,4,5], target=4 -> 2// [1,2,4,4,5], target=3 -> 2
Example: Upper bound (first > target)
function upperBound(nums, target) { let left = 0; let right = nums.length; while (left < right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] <= target) { left = mid + 1; } else { right = mid; } } return left;}// [1,2,4,4,5], target=4 -> 4// [1,2,4,4,5], target=3 -> 2
Example: Find first and last position of element
function searchRange(nums, target) { const findFirst = () => { let left = 0, right = nums.length - 1; let result = -1; while (left <= right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) { result = mid; right = mid - 1; // Keep searching left } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result; }; const findLast = () => { let left = 0, right = nums.length - 1; let result = -1; while (left <= right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) { result = mid; left = mid + 1; // Keep searching right } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return result; }; return [findFirst(), findLast()];}
3. Search in Rotated Sorted Array
Variant
Key Insight
Strategy
Complexity
Rotated Sorted Array
One half always sorted
Identify sorted half, check if target in range
O(log n)
With Duplicates
May need to skip duplicates
When arr[left]=arr[mid]=arr[right], shrink linearly
O(log n) avg, O(n) worst
Find Minimum
Minimum at rotation point
Compare mid with right, move towards unsorted half
O(log n)
Rotation Count
Index of minimum element
Same as find minimum, return index
O(log n)
Example: Search in rotated sorted array
function searchRotated(nums, target) { let left = 0; let right = nums.length - 1; while (left <= right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) return mid; // Determine which half is sorted if (nums[left] <= nums[mid]) { // Left half is sorted if (nums[left] <= target && target < nums[mid]) { right = mid - 1; // Target in left half } else { left = mid + 1; // Target in right half } } else { // Right half is sorted if (nums[mid] < target && target <= nums[right]) { left = mid + 1; // Target in right half } else { right = mid - 1; // Target in left half } } } return -1;}// [4,5,6,7,0,1,2], target=0 -> 4
Example: Find minimum in rotated sorted array
function findMin(nums) { let left = 0; let right = nums.length - 1; while (left < right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] > nums[right]) { // Minimum is in right half left = mid + 1; } else { // Minimum is in left half or at mid right = mid; } } return nums[left];}// [3,4,5,1,2] -> 1// [4,5,6,7,0,1,2] -> 0
4. Binary Search on Answer Space Pattern
Pattern
Problem Type
Search Space
Example Problems
Minimize Maximum
Find smallest value where condition holds
[min_possible, max_possible]
Split array largest sum, minimize max distance
Maximize Minimum
Find largest value where condition holds
[min_possible, max_possible]
Aggressive cows, maximize minimum distance
Feasibility Check
Binary search with validation function
Test if value is feasible
Koko eating bananas, capacity to ship packages
Monotonic Property
Answer space has monotone property
If k works, all values in direction also work
Essential for binary search applicability
Example: Koko eating bananas (minimize eating speed)
function minEatingSpeed(piles, h) { // Can Koko finish all piles in h hours at speed k? const canFinish = (k) => { let hours = 0; for (let pile of piles) { hours += Math.ceil(pile / k); } return hours <= h; }; let left = 1; // Minimum speed let right = Math.max(...piles); // Maximum speed needed while (left < right) { const mid = left + Math.floor((right - left) / 2); if (canFinish(mid)) { right = mid; // Try slower speed } else { left = mid + 1; // Need faster speed } } return left;}// piles=[3,6,7,11], h=8 -> 4
Example: Capacity to ship packages within D days
function shipWithinDays(weights, days) { // Can ship all packages in 'days' with capacity 'cap'? const canShip = (capacity) => { let daysNeeded = 1; let currentWeight = 0; for (let weight of weights) { if (currentWeight + weight > capacity) { daysNeeded++; currentWeight = 0; } currentWeight += weight; } return daysNeeded <= days; }; let left = Math.max(...weights); // Min capacity = max weight let right = weights.reduce((a, b) => a + b); // Max = sum all while (left < right) { const mid = left + Math.floor((right - left) / 2); if (canShip(mid)) { right = mid; // Try smaller capacity } else { left = mid + 1; // Need larger capacity } } return left;}
5. Floating Point Binary Search Precision
Aspect
Integer Binary Search
Floating Point Binary Search
Termination
left <= right
right - left > epsilon
Precision
Exact integer values
Threshold like 1e-6 or 1e-9
Iterations
log₂(n)
Fixed iterations (e.g., 100) or epsilon-based
Mid Update
mid = (left + right) / 2
mid = (left + right) / 2.0
Example: Square root with precision
function sqrt(x, precision = 1e-6) { if (x === 0) return 0; let left = 0; let right = x; // For x < 1, sqrt(x) > x if (x < 1) right = 1; while (right - left > precision) { const mid = (left + right) / 2; const square = mid * mid; if (square < x) { left = mid; } else { right = mid; } } return (left + right) / 2;}// sqrt(2) ≈ 1.414214// sqrt(0.25) ≈ 0.5
Example: Find peak element with fixed iterations
function findPeakElement(nums) { let left = 0; let right = nums.length - 1; while (left < right) { const mid = left + Math.floor((right - left) / 2); if (nums[mid] < nums[mid + 1]) { // Peak is on the right left = mid + 1; } else { // Peak is on the left or at mid right = mid; } } return left; // Peak index}// Works for integer arrays but demonstrates// the concept of moving towards increasing direction
6. Binary Search Edge Cases and Debugging
Common Bug
Symptom
Fix
Prevention
Infinite Loop
Loop never terminates
Check mid calculation, ensure progress
Use left + (right-left)/2, verify bounds update
Off-by-One
Missing boundary elements
Review < vs <=, mid±1
Test with arrays of size 1, 2, 3
Integer Overflow
Incorrect mid for large arrays
Never use (left+right)/2
Always: left + (right-left)/2
Wrong Condition
Returns incorrect result
Clarify what you're searching for
Write invariant: what does [left,right] contain?
Note: Common mistakes to avoid:
Using (left + right) / 2 - causes overflow
Not maintaining loop invariant
Incorrect boundary updates (infinite loop)
Forgetting edge cases (empty array, single element)
Warning: Debug checklist:
Does loop make progress every iteration?
Are boundaries updated correctly?
Is mid calculation overflow-safe?
Test with edge cases: [1], [1,2], [1,2,3]
Example: Debugging template with invariant comments
function binarySearchWithInvariant(nums, target) { let left = 0; let right = nums.length - 1; // Invariant: if target exists, it's in [left, right] while (left <= right) { // Prevent overflow const mid = left + Math.floor((right - left) / 2); if (nums[mid] === target) { return mid; // Found } // Maintain invariant: eliminate half that can't contain target if (nums[mid] < target) { left = mid + 1; // Target must be in (mid, right] } else { right = mid - 1; // Target must be in [left, mid) } } // Invariant broken: target doesn't exist return -1;}
Lower Bound: First position >= target, while(left<right)
Answer Space: Binary search on result values with feasibility check
Rotated Array: Identify sorted half, check if target in range
Key Insight: Eliminate half of search space each iteration = O(log n)
Note: Binary search requires monotonic property - some characteristic that increases/decreases across the search space. This applies to sorted arrays (element values increase) and answer spaces (feasibility changes at a threshold).