Binary Search Templates - TypeScript
Comprehensive binary search algorithm templates with detailed explanations and use cases.
📋 Table of Contents
- Template 1: Classic Binary Search
- Template 2: Left Boundary Search
- Template 3: Right Boundary Search
- Template 4: Generic Condition-Based Search
- Template 5: Rotated Array Search
- Template 6: Search in 2D Matrix
- Template 7: Binary Search on Answer
Template 1: Classic Binary Search
When to use: Finding exact target value in sorted array
Key characteristics:
- Returns index of target if found, -1 otherwise
- Works only on sorted arrays
- Uses
left <= rightcondition
typescript
/**
* Classic Binary Search - Find exact target
* Time: O(log n), Space: O(1)
*
* @param nums - Sorted array of numbers
* @param target - Value to search for
* @returns Index of target if found, -1 otherwise
*/
function classicBinarySearch(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}Template 2: Left Boundary Search
When to use: Finding the leftmost occurrence of target or insertion point
Key characteristics:
- Returns leftmost position where target can be inserted
- Works for finding first occurrence of duplicates
- Uses
left < rightcondition
typescript
/**
* Left Boundary Binary Search
* Time: O(log n), Space: O(1)
*
* Finds the leftmost position where target should be inserted
* or the first occurrence of target in array with duplicates
*
* @param nums - Sorted array
* @param target - Value to search for
* @returns Leftmost insertion position
*/
function leftBoundarySearch(nums: number[], target: number): number {
let left = 0;
let right = nums.length;
while (left < right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else {
// nums[mid] >= target, could be the answer
right = mid;
}
}
return left;
}
/**
* Alternative implementation for finding first occurrence
*/
function findFirstOccurrence(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
let result = -1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) {
result = mid;
right = mid - 1; // Continue searching left
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}Template 3: Right Boundary Search
When to use: Finding the rightmost occurrence of target or last valid position
Key characteristics:
- Returns rightmost position where target can be inserted
- Works for finding last occurrence of duplicates
- Uses
left < rightcondition
typescript
/**
* Right Boundary Binary Search
* Time: O(log n), Space: O(1)
*
* Finds the rightmost position where target should be inserted
* or the last occurrence of target in array with duplicates
*
* @param nums - Sorted array
* @param target - Value to search for
* @returns Rightmost insertion position
*/
function rightBoundarySearch(nums: number[], target: number): number {
let left = 0;
let right = nums.length;
while (left < right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] <= target) {
left = mid + 1;
} else {
// nums[mid] > target
right = mid;
}
}
return left - 1; // Adjust for rightmost valid position
}
/**
* Alternative implementation for finding last occurrence
*/
function findLastOccurrence(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
let result = -1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) {
result = mid;
left = mid + 1; // Continue searching right
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}Template 4: Generic Condition-Based Search
When to use: Finding minimum index where a condition becomes true
Key characteristics:
- Most flexible template for various binary search problems
- Condition function determines search direction
- Returns leftmost position where condition is true
typescript
/**
* Generic Condition-Based Binary Search
* Time: O(log n), Space: O(1)
*
* Finds the minimum index where condition(index) returns true
*
* @param left - Start of search range
* @param right - End of search range (exclusive)
* @param condition - Function that returns boolean based on index
* @returns Minimum index where condition is true
*/
function binarySearchByCondition(
left: number,
right: number,
condition: (mid: number) => boolean
): number {
while (left < right) {
const mid = left + Math.floor((right - left) / 2);
if (condition(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
/**
* Array-based condition search
*/
function arrayConditionSearch<T>(
nums: T[],
condition: (value: T, index: number) => boolean
): number {
let left = 0;
let right = nums.length;
while (left < right) {
const mid = left + Math.floor((right - left) / 2);
if (condition(nums[mid], mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}Template 5: Rotated Array Search
When to use: Searching in rotated sorted arrays
Key characteristics:
- Handles arrays rotated at unknown pivot
- Determines which half is sorted at each step
- Decides search direction based on sorted half
typescript
/**
* Search in Rotated Sorted Array
* Time: O(log n), Space: O(1)
*
* @param nums - Rotated sorted array
* @param target - Value to search for
* @returns Index of target if found, -1 otherwise
*/
function searchRotatedArray(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) {
return mid;
}
// Determine which half is sorted
if (nums[left] <= nums[mid]) {
// Left half is sorted
if (nums[left] <= target && target < nums[mid]) {
// Target is in sorted left half
right = mid - 1;
} else {
// Target is in right half
left = mid + 1;
}
} else {
// Right half is sorted
if (nums[mid] < target && target <= nums[right]) {
// Target is in sorted right half
left = mid + 1;
} else {
// Target is in left half
right = mid - 1;
}
}
}
return -1;
}
/**
* Find minimum in rotated sorted array
*/
function findMinInRotatedArray(nums: number[]): number {
let left = 0;
let right = nums.length - 1;
while (left < right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] > nums[right]) {
// Minimum is in right half
left = mid + 1;
} else {
// Minimum is in left half (including mid)
right = mid;
}
}
return nums[left];
}Template 6: Search in 2D Matrix
When to use: Searching in sorted 2D matrices
Key characteristics:
- Treats 2D matrix as flattened 1D array
- Converts between 1D and 2D coordinates
- Maintains binary search properties
typescript
/**
* Search in 2D Matrix (sorted row-wise and column-wise)
* Time: O(log(m*n)), Space: O(1)
*
* @param matrix - 2D sorted matrix
* @param target - Value to search for
* @returns True if target exists, false otherwise
*/
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 = left + Math.floor((right - left) / 2);
// Convert 1D index to 2D coordinates
const row = Math.floor(mid / cols);
const col = mid % cols;
const midValue = matrix[row][col];
if (midValue === target) {
return true;
} else if (midValue < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
/**
* Search in matrix where each row and column is sorted
* Time: O(m + n), Space: O(1)
*/
function searchMatrixII(matrix: number[][], target: number): boolean {
if (matrix.length === 0 || matrix[0].length === 0) {
return false;
}
let row = 0;
let col = matrix[0].length - 1;
// Start from top-right corner
while (row < matrix.length && col >= 0) {
if (matrix[row][col] === target) {
return true;
} else if (matrix[row][col] > target) {
col--; // Move left
} else {
row++; // Move down
}
}
return false;
}Template 7: Binary Search on Answer
When to use: Optimization problems where you search for optimal value
Key characteristics:
- Search space is the range of possible answers
- Uses condition function to check feasibility
- Finds minimum/maximum valid answer
typescript
/**
* Binary Search on Answer Template
* Time: O(log(max-min) * O(check)), Space: O(1)
*
* @param minAnswer - Minimum possible answer
* @param maxAnswer - Maximum possible answer
* @param isValid - Function to check if answer is valid
* @param findMinimum - True for minimum valid answer, false for maximum
* @returns Optimal answer
*/
function binarySearchOnAnswer(
minAnswer: number,
maxAnswer: number,
isValid: (answer: number) => boolean,
findMinimum: boolean = true
): number {
let left = minAnswer;
let right = maxAnswer;
let result = findMinimum ? maxAnswer : minAnswer;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (isValid(mid)) {
result = mid;
if (findMinimum) {
right = mid - 1; // Look for smaller valid answer
} else {
left = mid + 1; // Look for larger valid answer
}
} else {
if (findMinimum) {
left = mid + 1; // Need larger answer
} else {
right = mid - 1; // Need smaller answer
}
}
}
return result;
}
/**
* Example: Find square root using binary search
*/
function mySqrt(x: number): number {
if (x < 2) return x;
return binarySearchOnAnswer(
1,
x,
(mid) => mid * mid <= x,
false // Find maximum valid answer
);
}Helper Functions
typescript
/**
* Convert 1D index to 2D coordinates
*/
function indexTo2D(index: number, cols: number): [number, number] {
return [Math.floor(index / cols), index % cols];
}
/**
* Convert 2D coordinates to 1D index
*/
function coordinatesToIndex(row: number, col: number, cols: number): number {
return row * cols + col;
}
/**
* Check if binary search invariant is maintained
*/
function validateBinarySearchInvariant<T>(
nums: T[],
left: number,
right: number,
condition: (value: T) => boolean
): boolean {
// All elements before left should not satisfy condition
for (let i = 0; i < left; i++) {
if (condition(nums[i])) return false;
}
// All elements from left onwards should satisfy condition
for (let i = left; i < nums.length; i++) {
if (!condition(nums[i])) return false;
}
return true;
}Template Selection Guide
| Problem Type | Template | Key Insight |
|---|---|---|
| Find exact value | Template 1 | Classic binary search |
| Find first occurrence | Template 2 | Search left boundary |
| Find last occurrence | Template 3 | Search right boundary |
| Custom condition | Template 4 | Most flexible approach |
| Rotated array | Template 5 | Identify sorted half |
| 2D matrix | Template 6 | Flatten or corner approach |
| Optimization | Template 7 | Search answer space |
Common Pitfalls and Best Practices
1. Integer Overflow Prevention
typescript
// ❌ Dangerous: may overflow
const mid = Math.floor((left + right) / 2);
// ✅ Safe: prevents overflow
const mid = left + Math.floor((right - left) / 2);2. Loop Termination Conditions
typescript
// For exact search
while (left <= right) { ... }
// For boundary search
while (left < right) { ... }3. Index Updates
typescript
// When condition is true (searching for minimum)
right = mid;
// When condition is false
left = mid + 1;4. Return Value Considerations
typescript
// For existence check
return found ? index : -1;
// For insertion point
return left;
// For boundary search
return left; // or right, depending on template