Skip to main content

Binary Search and Its Variants

Binary Search is a classic algorithm for finding an element in a sorted array. It works by repeatedly dividing the search interval in half. If the value of the search key is less than the item in the middle of the interval, narrow the interval to the lower half. Otherwise, narrow it to the upper half. Repeatedly check until the value is found or the interval is empty. Both low + Math.floor((high - low) / 2) and Math.floor(left + (right - left) / 2) are valid ways to compute the mid index in binary search and can be used to avoid integer overflow.

Here is a basic implementation of Binary Search in JavaScript:

function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;

while (left <= right) {
const mid = Math.floor((left + right) / 2);

if (arr[mid] === target) {
return mid; // Target found at index mid
} else if (arr[mid] < target) {
left = mid + 1; // Search in the right half
} else {
right = mid - 1; // Search in the left half
}
}

return -1; // Target not found
}

// Example usage
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const target = 5;
console.log(binarySearch(nums, target)); // Output: 4

Finds the first position where the target could be inserted without violating the order.


const findLowerBound = (nums = [], target) => {
let left = 0;
let right = nums.length;

while (left < right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
};

Finds the first position where an element greater than the target appears.

const findUpperBound = (nums = [], target) => {
let left = 0;
let right = nums.length;

while (left < right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
};

Finds the index of the smallest element greater than the target.

const findLeastGreater = (nums, key) => {
const n = nums.length;
let low = 0;
let high = n - 1;
let result = -1;

while (low <= high) {
const mid = low + Math.floor((high - low) / 2);
if (nums[mid] > key) {
result = mid;
high = mid - 1;
} else low = mid + 1;
}
return result;
};

Finds the index of the largest element less than the target.

const findGreatestLess = (nums, key) => {
const n = nums.length;
let low = 0;
let high = n - 1;
let result = -1;

while (low <= high) {
const mid = low + Math.floor((high - low) / 2);
if (nums[mid] < key) {
result = mid;
low = mid + 1;
} else high = mid - 1;
}
return result;
};

Finds the index at which a target value should be inserted to maintain the sorted order.

const searchInsert = (nums, target) => {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
};

Find Minimum in a Rotated Sorted Array

const findMin = (nums) => {
let left = 0;
let right = nums.length - 1;
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
};

Search in Rotated Sorted Array

const search = (nums, target) => {
let left = 0;
let right = nums.length - 1;

while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
};

Binary Search on Answer Pattern:

function binarySearchOnAnswer(rangeStart, rangeEnd, isValid) {
let left = rangeStart;
let right = rangeEnd;

while (left < right) {
const mid = Math.floor((left + right) / 2);

if (isValid(mid)) {
right = mid; // mid might be the answer, search in the left half
} else {
left = mid + 1; // mid is too small, search in the right half
}
}

return left; // or right, as left == right
}

// Example usage for a problem
const isValid = (value) => {
// Define what makes a value valid in your specific problem
// Return true if value meets the condition, false otherwise
};

Binary Search on Answer Pattern Problems

Binary Search on 2D Matrix:

Consider a 2D matrix:

matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60] ] This matrix has 3 rows and 4 columns.

Row Calculation: Math.floor(mid / cols) Here, mid is the current index in the flattened perspective, and cols is the number of columns in each row of the matrix This division gives the number of full rows completed before reaching the current element Column Calculation: The column number is calculated as mid % cols. The modulus operation gives the remainder, which corresponds to the column index within the row. Explanation with an Example Let's take an example where mid = 5 and cols = 4.

Row Calculation:

Math.floor(mid / cols) = Math.floor(5 / 4) = 1 This indicates that the element at index 5 in the flattened view is in row 1 of the matrix (0-based index). Column Calculation:

mid % cols = 5 % 4 = 1 This indicates that the element is at column 1 in the matrix (0-based index). Therefore, matrix[Math.floor(mid / cols)][mid % cols] corresponds to matrix[1][1], which is 11 in the given matrix.

function searchMatrix(matrix, target) {
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 midValue = matrix[Math.floor(mid / cols)][mid % cols];

if (midValue === target) {
return true;
} else if (midValue < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return false;
}

// Example usage
const matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 60]
];
const target = 3;
const result = searchMatrix(matrix, target);
console.log(result); // Outputs: true