# 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 ```typescript 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) ```typescript 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) ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 = 0` and `right = arr.length - 1` - For exclusive bounds: `left = 0` and `right = arr.length` ### 2. Mid Calculation ```typescript // 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 ```typescript // Include equal case to find target while (left <= right) // For exclusive bounds while (left < right) ``` ### 4. Pointer Updates ```typescript // Always move mid pointer left = mid + 1; // Not mid right = mid - 1; // Not mid ``` ## Common Mistakes ### 1. Infinite Loop ```typescript // 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 ```typescript // Wrong: potential overflow const mid = (left + right) / 2; // Correct: safer approach const mid = left + Math.floor((right - left) / 2); ``` ### 3. Wrong Loop Condition ```typescript // 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 ```typescript // 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 1. **Database Indexes**: B-tree searches 2. **Game Development**: Collision detection, spatial partitioning 3. **Machine Learning**: Decision trees, feature selection 4. **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 1. Master basic binary search (5+ problems) 2. Practice rotated array variations 3. Learn to apply binary search to search spaces 4. 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.