Files

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 = 0 and right = arr.length - 1
  • For exclusive bounds: left = 0 and right = 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

  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
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.