Binary Search Algorithm
Binary search is an efficient divide and conquer search algorithm that finds a target value in a sorted array by repeatedly dividing the search interval in half. It's much faster than linear search for large datasets, but requires the array to be sorted.
Interactive Visualization
Binary Search Visualization
Sorted Array Visualization
How Binary Search Works:
• Divide: Calculate mid point of current search range
• Compare: Check if mid element equals, is less than, or greater than target
• Conquer: Discard half of the array based on comparison
• Repeat: Continue until target is found or range is empty
• Time Complexity: O(log n) - eliminates half each step
• Space Complexity: O(1) - only uses a few variables
• Prerequisite: Array must be sorted
• Efficiency: Much faster than linear search for large arrays
Divide and Conquer Strategy
Binary search exemplifies the divide and conquer paradigm by systematically eliminating half of the remaining search space in each iteration. This logarithmic reduction makes it incredibly efficient for large sorted datasets.
1. Divide
Find the middle element of the current search range
2. Compare
Compare the middle element with the target value
3. Conquer
Eliminate half the search space based on the comparison
🔑 Key Insight
By comparing with the middle element, we can determine which half of the array can contain our target (if it exists at all). This allows us to discard the other half entirely, reducing the problem size by 50% in each step.
⚠️ Critical Requirement: Sorted Array
Binary search only works on sorted arrays. This is not just a recommendation – it's a fundamental requirement for the algorithm's correctness.
Why Sorting is Essential
- • Predictable Ordering: Comparison with middle element must indicate which half contains the target
- • Elimination Logic: If middle > target, we know target can only be in the left half
- • Search Space Reduction: Each comparison eliminates exactly half of the remaining elements
- • Termination Guarantee: Algorithm will definitely terminate with correct result
Without Sorting
- • Incorrect Results: May miss elements that are actually present
- • False Positives: May incorrectly conclude element is absent
- • No Performance Benefit: Cannot reliably eliminate half the search space
- • Algorithm Breakdown: The fundamental assumption is violated
Example: Searching for 7
How Binary Search Works
Step-by-Step Process
Case 1: Found
If arr[mid] == target:
Return mid (we found it!)
Case 2: Go Left
If arr[mid] > target:
Set high = mid - 1
(Target must be in left half)
Case 3: Go Right
If arr[mid] < target:
Set low = mid + 1
(Target must be in right half)
Pseudocode
ALGORITHM BinarySearch(arr[], n, target)
BEGIN
// Initialize search bounds
low = 0
high = n - 1
// Continue while search space is valid
WHILE low <= high DO
// Calculate middle index (avoid overflow)
mid = low + (high - low) / 2
// Check if middle element is the target
IF arr[mid] == target THEN
RETURN mid // Found at index mid
// Target is in the left half
ELSE IF arr[mid] > target THEN
high = mid - 1 // Eliminate right half
// Target is in the right half
ELSE
low = mid + 1 // Eliminate left half
END IF
END WHILE
// Target not found in array
RETURN -1
END
// Recursive version
ALGORITHM BinarySearchRecursive(arr[], target, low, high)
BEGIN
// Base case: invalid range
IF low > high THEN
RETURN -1
END IF
// Calculate middle index
mid = low + (high - low) / 2
// Found the target
IF arr[mid] == target THEN
RETURN mid
// Search in left half
ELSE IF arr[mid] > target THEN
RETURN BinarySearchRecursive(arr, target, low, mid - 1)
// Search in right half
ELSE
RETURN BinarySearchRecursive(arr, target, mid + 1, high)
END IF
END Complexity Analysis
| Metric | Iterative | Recursive | Explanation |
|---|---|---|---|
| Time Complexity | O(log n) | O(log n) | Eliminates half the elements each step |
| Space Complexity | O(1) | O(log n) | Iterative uses constant space; recursive uses call stack |
| Max Comparisons | ⌈log₂ n⌉ | ⌈log₂ n⌉ | At most log base 2 of n comparisons needed |
Why O(log n) Time?
- • Half Elimination: Each comparison eliminates half the remaining elements
- • Logarithmic Depth: Takes at most ⌈log₂ n⌉ steps to reduce n elements to 1
- • Example: Array of 1000 elements needs at most 10 comparisons (2¹⁰ = 1024)
- • Massive Efficiency: 1 million elements → only 20 comparisons!
Space Complexity Comparison
- • Iterative O(1): Only uses a few variables (low, high, mid)
- • Recursive O(log n): Each recursive call adds to the call stack
- • Maximum Depth: Recursion depth equals number of iterations
- • Preference: Iterative version is usually preferred for space efficiency
Binary Search vs Linear Search
Linear: 500 avg comparisons
Binary: 10 max comparisons
50x faster!
Linear: 500,000 avg comparisons
Binary: 20 max comparisons
25,000x faster!
Linear: 500,000,000 avg comparisons
Binary: 30 max comparisons
16,000,000x faster!
Code Examples
Here are implementations of binary search in different programming languages. Each version includes both iterative and recursive approaches, along with generic implementations and overflow-safe middle index calculation.
def binary_search(arr, target):
"""
Search for a target value in a sorted array using binary search.
Args:
arr: Sorted list of elements to search through
target: The value to search for
Returns:
int: Index of target if found, -1 if not found
Time Complexity: O(log n)
Space Complexity: O(1)
Prerequisite: Array must be sorted
"""
left = 0
right = len(arr) - 1
while left <= right:
# Calculate middle index (avoid overflow in other languages)
mid = left + (right - left) // 2
# Found the target
if arr[mid] == target:
return mid
# Target is in the left half
elif arr[mid] > target:
right = mid - 1
# Target is in the right half
else:
left = mid + 1
# Target not found
return -1
def binary_search_recursive(arr, target, left=0, right=None):
"""
Recursive implementation of binary search.
Space Complexity: O(log n) due to recursion stack
"""
if right is None:
right = len(arr) - 1
# Base case: invalid range
if left > right:
return -1
# Calculate middle index
mid = left + (right - left) // 2
# Found the target
if arr[mid] == target:
return mid
# Target is in the left half
elif arr[mid] > target:
return binary_search_recursive(arr, target, left, mid - 1)
# Target is in the right half
else:
return binary_search_recursive(arr, target, mid + 1, right)
# Example usage
if __name__ == "__main__":
# Array must be sorted for binary search to work
sorted_numbers = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
target = 7
result = binary_search(sorted_numbers, target)
if result != -1:
print(f"Element {target} found at index {result}")
else:
print(f"Element {target} not found in the array")
# Test with recursive version
result_recursive = binary_search_recursive(sorted_numbers, 15)
print(f"Recursive search result for 15: index {result_recursive}") When to Use Binary Search
Perfect For
- • Large Sorted Arrays: Massive performance advantage over linear search
- • Frequent Searches: When you need to search the same dataset multiple times
- • Database Indices: Foundation of many database indexing strategies
- • Range Queries: Finding insertion points, lower/upper bounds
- • Memory-Efficient Searches: O(1) space complexity with iterative version
Consider Alternatives When
- • Unsorted Data: Sorting cost may outweigh search benefits
- • Small Arrays: Linear search might be simpler and fast enough
- • Frequent Insertions: Maintaining sorted order becomes expensive
- • One-time Search: Sorting overhead not justified for single search