8.2 KiB
8.2 KiB
Binary Search Pattern
Overview
Binary search is a fundamental algorithm for finding elements in sorted arrays. It follows the divide-and-conquer strategy, repeatedly dividing the search space in half.
Core Concepts
Binary Search Principle
- Precondition: Array must be sorted
- Process: Compare target with middle element, eliminate half of search space
- Complexity: O(log n) time, O(1) space
Basic Template
function binarySearch(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1; // Target not found
}
Variations
1. Lower Bound (First Occurrence)
function lowerBound(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] >= target) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
2. Upper Bound (Last Occurrence)
function upperBound(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] <= target) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
3. Find Closest Element
function findClosest(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
while (left < right - 1) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] < target) {
left = mid;
} else {
right = mid;
}
}
// Compare the two remaining elements
return Math.abs(arr[left] - target) <= Math.abs(arr[right] - target)
? arr[left] : arr[right];
}
Advanced Applications
1. Search in Rotated Sorted Array
function search(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
// Check if left half is sorted
if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// Right half is sorted
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
2. Find Minimum in Rotated Sorted Array
function findMin(nums: number[]): number {
let left = 0;
let right = nums.length - 1;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
3. Search in 2D Matrix
function searchMatrix(matrix: number[][], target: number): boolean {
if (matrix.length === 0 || matrix[0].length === 0) return false;
const rows = matrix.length;
const cols = matrix[0].length;
let left = 0;
let right = rows * cols - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const row = Math.floor(mid / cols);
const col = mid % cols;
if (matrix[row][col] === target) {
return true;
} else if (matrix[row][col] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
Time Complexity Analysis
| Problem | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Basic Binary Search | O(log n) | O(1) | Standard implementation |
| Lower Bound | O(log n) | O(1) | First occurrence |
| Upper Bound | O(log n) | O(1) | Last occurrence |
| Rotated Array | O(log n) | O(1) | Handle rotation |
| 2D Matrix | O(log(mn)) | O(1) | Treat as 1D |
Best Practices
1. Pointer Initialization
- Use
left = 0andright = arr.length - 1 - For exclusive bounds:
left = 0andright = arr.length
2. Mid Calculation
// Avoid overflow in some languages
const mid = left + Math.floor((right - left) / 2);
// Standard approach in TypeScript
const mid = Math.floor((left + right) / 2);
3. Loop Condition
// Include equal case to find target
while (left <= right)
// For exclusive bounds
while (left < right)
4. Pointer Updates
// Always move mid pointer
left = mid + 1; // Not mid
right = mid - 1; // Not mid
Common Mistakes
1. Infinite Loop
// Wrong: mid might not move
left = mid; // Should be mid + 1
right = mid; // Should be mid - 1
// Correct: move pointers past mid
left = mid + 1;
right = mid - 1;
2. Incorrect Mid Calculation
// Wrong: potential overflow
const mid = (left + right) / 2;
// Correct: safer approach
const mid = left + Math.floor((right - left) / 2);
3. Wrong Loop Condition
// Wrong: might miss element
while (left < right) {
// Target might be at right boundary
// Correct: include equal case
while (left <= right) {
// Ensures all elements are checked
}
4. Not Handling Edge Cases
// Wrong: empty array
while (left <= right) {
// Will fail for empty array
// Correct: add check
if (arr.length === 0) return -1;
Practice Problems
Easy
- Basic binary search
- Find lower bound
- Find upper bound
- Find closest element
- Find peak in mountain array
Medium
- Search in rotated sorted array
- Find minimum in rotated sorted array
- Search in 2D matrix
- Find first and last position of element
- Find peak element
Hard
- Median of two sorted arrays
- Find k-th element in sorted matrix
- Find smallest letter greater than target
- Find in mountain array
- Find frequency of element in sorted array
Real-world Applications
- Database Indexes: B-tree searches
- Game Development: Collision detection, spatial partitioning
- Machine Learning: Decision trees, feature selection
- Operating Systems: Process scheduling, resource allocation
Binary Search vs Linear Search
| Aspect | Binary Search | Linear Search |
|---|---|---|
| Time Complexity | O(log n) | O(n) |
| Space Complexity | O(1) | O(1) |
| Precondition | Array must be sorted | No precondition |
| Best for | Large sorted datasets | Small or unsorted datasets |
| Insertion | O(n) | O(1) |
Tips for Mastery
1. Understand the Pattern
- Binary search works on any monotonic function
- It's not just for arrays, but any searchable space
- The key is reducing search space by half each time
2. Practice Edge Cases
- Empty array
- Single element
- All elements same
- Target not present
- Target at boundaries
3. Implement Variations
- Practice lower/upper bounds
- Work with rotated arrays
- Try 2D searches
- Implement on custom data structures
4. Combine with Other Patterns
- Binary search + two pointers
- Binary search + sliding window
- Binary search + dynamic programming
Next Steps
- Master basic binary search (5+ problems)
- Practice rotated array variations
- Learn to apply binary search to search spaces
- Combine with other algorithmic patterns
Key Takeaway: Binary search is one of the most important algorithms. It's efficient, elegant, and applicable to many problems beyond simple array searches. Always consider binary search when dealing with sorted data.