public int binarySearch(int[] nums, int target) { int left = 0, right = nums.length; // right = n, not n-1 while (left < right) { // left < right, not <= int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { right = mid; // not mid - 1 } } return left; // or right, they're equal}
public int binarySearch(int[] nums, int target) { int left = -1, right = nums.length; while (left + 1 < right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid; } else { right = mid; } } return right;}
public int lowerBound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return left;}
Finding Variations
First greater than target: lowerBound(nums, target + 1)
Last less than target: lowerBound(nums, target) - 1
Last less than or equal to target: lowerBound(nums, target + 1) - 1
public int upperBound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] <= target) { left = mid + 1; } else { right = mid - 1; } } return left;}
LeetCode 162: Find a peak element in an array where nums[i] > nums[i-1] and nums[i] > nums[i+1].
class Solution { public int findPeakElement(int[] nums) { int left = 0, right = nums.length - 1; while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] > nums[mid + 1]) { // Peak is on the left side (including mid) right = mid; } else { // Peak is on the right side left = mid + 1; } } return left; }}
Why This Works
If nums[mid] > nums[mid + 1], there must be a peak on the left (including mid) because:
If all elements to the left decrease, nums[0] is a peak (since nums[-1] = -∞)
Otherwise, there’s an increase followed by decrease, forming a peak
class Solution { public int search(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return mid; } // Determine which half is sorted if (nums[mid] >= nums[left]) { // Left half is sorted 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; }}
Key Insight: At least one half of the array is always sorted. We can determine which half and check if the target lies in that sorted half.
class Solution { public int minSpeedOnTime(int[] dist, double hour) { int left = 1, right = (int) 1e7; int result = -1; while (left <= right) { int mid = left + (right - left) / 2; if (canArrive(dist, hour, mid)) { result = mid; right = mid - 1; // Try smaller speed } else { left = mid + 1; // Need larger speed } } return result; } private boolean canArrive(int[] dist, double hour, int speed) { double time = 0; for (int i = 0; i < dist.length - 1; i++) { time += Math.ceil((double) dist[i] / speed); } time += (double) dist[dist.length - 1] / speed; return time <= hour; }}