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