sorted search space
with two
pointers
partial sorted
,
rotated sorted
is possible as wellinclude all possible cases
Types
Basic binary search
Search in rotate
array
Minimum
in Rotated Sorted Array
// java
// ...
while(r >= l){
int mid = (l + r) / 2;
// case 1) if `left subarray + mid` is ascending
if(nums[mid] >= nums[l]){
= mid + 1;
l }
// case 2) if `right subarray + mid` is ascending
else{
= mid - 1;
r }
// ...
LC 33, LC 81
``java // java // ... while (r >= l){ int mid = (l + r) / 2; int cur = nums[mid]; if (cur == target){ return mid; } // Case 1:
left
+
midis in ascending order /** NOTE !!! we compare mid with left, instead of 0 idx element */ else if (nums[mid] >= nums[l]) { /** NOTE !!! * * 1) ">=" * 2) since
if
nums[mid] == target`, we should already FOUND a solution * within code
above, so here, we use target < nums[mid], * but for left boundary,
we use “>=”. e.g. target >= nums[l] */ // case 1-1) target <
mid && target > l if (target >= nums[l] && target
< nums[mid]) { r = mid - 1; } // case 2-2) else { l = mid + 1; }
}
// Case 2: right + mid
is in ascending order else { /**
NOTE !!! 1) “<=” * 2) since
if nums[mid] == target
, we should already FOUND a solution
* within code above, so here, we use target > nums[mid], * but for
right boundary, we use “<=”. e.g. target <= nums[r] */ // case
2-1) target > min && target <= r if (target <= nums[r]
&& target > nums[mid]) { l = mid + 1; } // case 2-2) else { r
= mid - 1; } }
// … ```
min
, so ONLY 1
if-else
is enoughtarget
, so need 2
if-else logicRecursive
binary search
Search in 2D array
flatten
array with x % y = z
handling, or
visit row by row via binary searchFind min in Rotation array
mid
point is at left
or
right
sub array
Find start, end idx with target
// java
// ...
int mid = (l + r) / 2;
while (r >= l){
if (nums[mid] == target){
/**
* NOTE !!! below
*
* instead of quit while loop directly,
* we
* 1) keep finding `left idx` if want to find first idx
* 2) keep finding `right idx` if want to find last idx
*
*/
if(findFirst){
= mid - 1;
r }else{
= mid + 1;
l }
}else if (nums[mid] < target){
= mid + 1;
l }else{
= mid - 1;
r }
}
// ...
Find LEFT
boundary
mid == target
, final check# python
while r >= l:
= (l + r) // 2
mid if mid < target:
= mid + 1
l else if mid > target:
= mid - 1
r else if mid == target:
= mid - 1 # reduce right boundary,
r if l >= nums.length or nums[l] != target: # check if l is out of boundary
return -1
return l
// java
// ...
while (r >= l){
= (l + r) / 2;
mid if (mid <= some_value){
= mid - 1;
r }else{
= mid + 1;
l }
}
// ...
Find RIGHT
boundary
mid == target
, final check# python
while r >= l:
= (l + r) // 2
mid if mid < target:
= mid + 1
l else if mid > target:
= mid - 1
r else if mid == target:
= mid + 1 # reduce left boundary
l if r < 0 or nums[r] != target: # check if r is out of boundary
return -1
return r
Algorithm
Data structure
left = 0, right = nums.len - 1
while left <= right
left = mid + 1
right = mid - 1
分析二分查找的一個技巧是:不要出現 else,而是把所有情況用 else if 寫清楚,這樣可以清楚地展現所有細節。本文都會使用 else if,旨在講清楚,讀者理解後可自行簡化。
// java
int binarySearch(int[] nums, int target) {
int left = 0;
// 注意
int right = nums.length - 1;
// NOTE !!! <=, need to search when "left == right" idx as well
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
// 注意
= mid + 1;
left else if (nums[mid] > target)
// 注意
= mid - 1;
right }
return -1;
}
# python
# basic
def binary_search(nums, target):
= 0
l = len(nums) - 1
r """
NOTE : WE ALWALYS USE CLOSED boundary (for logic unifying)
-> e.g. while l <= r
-> [l, r]
"""
while l <= r:
= l + (r-l)//2
mid if nums[mid] == target:
return mid
elif nums[mid] < target:
### NOTE this
= mid+1
l else:
### NOTE this
= mid-1
r return -1
LEFT
boundary
/**
* 2 difference between regular binary search
*
* 1. else if (nums[mid] == target) {
* // 收缩右侧边界
* right = mid - 1;
* }
*
*
*
* 2. validate result step
*
* if (left < 0 || left >= nums.length) {
* return -1;
* }
* // 判断一下 nums[left] 是不是 target
* return nums[left] == target ? left : -1;
*
*
*/
// java
int left_bound(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
// NOTE : WE ALWALYS USE CLOSED boundary (for logic unifying)
// -> e.g. while l <= r
// -> [l, r]
while (left <= right){
int mid = left + (right - mid) / 2;
if (num[mid] < target){
// 搜索区间变为 [mid+1, right]
= mid + 1;
left }else if (nums[mid] > target){
// 搜索区间变为 [left, mid-1]
= mid - 1;
right }else if (nums[mid] == target){
// DO NOT RETURN !!!, BUT REDUCE RIGHT BOUNDARY FOR FOCUSING ON LEFT BOUNDARY
// 收缩右侧边界
= mid - 1;
right }
}
// // finally check if it will be OUT OF LEFT boundary
// if (left >= nums.length || nums[left] != target){
// return -1;
// return left;
// 判断 target 是否存在于 nums 中
// 如果越界,target 肯定不存在,返回 -1
if (left < 0 || left >= nums.length) {
return -1;
}
// 判断一下 nums[left] 是不是 target
return nums[left] == target ? left : -1;
}
# python
def binary_search_left_boundary(nums, target):
= 0
l = len(nums) - 1
r """
NOTE : WE ALWALYS USE CLOSED boundary (for logic unifying)
-> e.g. while l <= r
-> [l, r]
"""
while l <= r:
= l + (r-l)//2
mid # DO NOT RETURN !!!, BUT REDUCE RIGHT BOUNDARY FOR FOCUSING ON LEFT BOUNDARY
if nums[mid] == target:
= mid - 1
r elif nums[mid] < target:
### NOTE this
= mid + 1
l else:
### NOTE this
= mid - 1
r # finally check if it will be OUT OF LEFT boundary
if l >= len(nums) or nums[l] != target:
return - 1
return l
RIGHT
boundary// java
/**
* 2 difference between regular binary search
*
* 1. else if (nums[mid] == target) {
* // 收缩左侧边界
* left = mid + 1;
* }
*
*
*
* 2. validate result step
*
* // 最后改成返回 left - 1
* if (left - 1 < 0 || left - 1 >= nums.length) {
* return -1;
* }
* return nums[left - 1] == target ? (left - 1) : -1;
*
*
*/
int right_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
= mid + 1;
left } else if (nums[mid] > target) {
= mid - 1;
right } else if (nums[mid] == target) {
// 这里改成收缩左侧边界即可
= mid + 1;
left }
}
// 最后改成返回 left - 1
if (left - 1 < 0 || left - 1 >= nums.length) {
return -1;
}
return nums[left - 1] == target ? (left - 1) : -1;
}
# python
def binary_search_right_boundary(nums, target):
= 0
l = len(nums) - 1
r """
NOTE : WE ALWALYS USE CLOSED boundary (for logic unifying)
-> e.g. while l <= r
-> [l, r]
"""
while l <= r:
= l + (r-l)//2
mid # DO NOT RETURN !!!, BUT REDUCE LEFT BOUNDARY FOR FOCUSING ON RIGHT BOUNDARY
if nums[mid] == target:
= mid + 1
l elif nums[mid] < target:
### NOTE this
= mid + 1
l else:
### NOTE this
= mid - 1
r # finally check if it will be OUT OF RIGHT boundary
if r < 0 or nums[r] != target:
return - 1
return r
# LC 033. Search in Rotated Sorted Array
# LC 081. Search in Rotated Sorted Array II
# V0
# IDEA : BINARY SEARCH
# -> CHECK WHICH PART IS ORDERING
# -> CHECK IF TARGET IS IN WHICH PART
# CASES :
# 1) if mid is on the right of pivot -> array[mid:] is ordering
# -> check if mid in on the left or right on mid
# -> binary search on left or right sub array
# 2) if mid in on the left of pivot -> array[:mid] is ordering
# -> check if mid in on the left or right on mid
# -> binary search on left or right sub array
### NOTE : THE NESTED IF ELSE CONDITION
class Solution(object):
def search(self, nums, target):
if not nums: return -1
= 0, len(nums) - 1
left, right while left <= right:
= (left + right) // 2
mid if nums[mid] == target:
return mid
#---------------------------------------------
# Case 1 : nums[mid:right] is ordering
#---------------------------------------------
# all we need to do is : 1) check if target is within mid - right, and move the left or right pointer
if nums[mid] < nums[right]:
# mind NOT use (" nums[mid] < target <= nums[right]")
# mind the "<="
if target > nums[mid] and target <= nums[right]: # check the relationship with target, which is different from the default binary search
= mid + 1
left else:
= mid - 1
right #---------------------------------------------
# Case 2 : nums[left:mid] is ordering
#---------------------------------------------
# all we need to do is : 1) check if target is within left - mid, and move the left or right pointer
else:
# # mind NOT use (" nums[left] <= target < nums[mid]")
# mind the "<="
if target < nums[mid] and target >= nums[left]: # check the relationship with target, which is different from the default binary search
= mid - 1
right else:
= mid + 1
left return -1
// java
// LC 33
// V3
// IDEA : One Binary Search
// https://leetcode.com/problems/search-in-rotated-sorted-array/editorial/
public int search_4(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// Case 1: find target
if (nums[mid] == target) {
return mid;
}
// Case 2: subarray on mid's left is sorted
else if (nums[mid] >= nums[left]) {
if (target >= nums[left] && target < nums[mid]) {
= mid - 1;
right } else {
= mid + 1;
left }
}
// Case 3: subarray on mid's right is sorted
else {
if (target <= nums[right] && target > nums[mid]) {
= mid + 1;
left } else {
= mid - 1;
right }
}
}
return -1;
}
# 167 Two Sum II - Input array is sorted
class Solution(object):
def twoSum(self, numbers, target):
for i in range(len(numbers)):
= i+1, len(numbers)-1
l, r = target - numbers[i]
tmp while l <= r:
= l + (r-l)//2
mid if numbers[mid] == tmp:
return [i+1, mid+1]
elif numbers[mid] < tmp:
= mid+1
l else:
= mid-1 r
# LC 162 Find Peak Element, LC 852 Peak Index in a Mountain Array
# V0'
# IDEA : RECURSIVE BINARY SEARCH
class Solution(object):
def findPeakElement(self, nums):
def help(nums, l, r):
if l == r:
return l
= l + (r - l) // 2
mid if (nums[mid] > nums[mid+1]):
return help(nums, l, mid) # r = mid
return help(nums, mid+1, r) # l = mid + 1
return help(nums, 0, len(nums)-1)
// java
// LC 162
// V2
// IDEA: RECURSIVE BINARY SEARCH
// https://leetcode.com/problems/find-peak-element/editorial/
// NOTE : ONLY have to compare index i with index i + 1 (its right element)
// ; otherwise, i-1 already returned as answer
public int findPeakElement_2(int[] nums) {
return search(nums, 0, nums.length - 1);
}
public int search(int[] nums, int l, int r) {
if (l == r)
return l;
int mid = (l + r) / 2;
if (nums[mid] > nums[mid + 1])
return search(nums, l, mid);
return search(nums, mid + 1, r);
}
// V3
// IDEA: ITERATIVE BINARY SEARCH
// https://leetcode.com/problems/find-peak-element/editorial/
public int findPeakElement_3(int[] nums) {
int l = 0, r = nums.length - 1;
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] > nums[mid + 1])
= mid;
r else
= mid + 1;
l }
return l;
}
# 367 Valid Perfect Square, LC 69 Sqrt(x)
# V0'
# IDEA : BINARY SEARCH
class Solution(object):
def isPerfectSquare(self, num):
= 0, num
left, right while left <= right:
### NOTE : there is NO mid * mid == num condition
= (left + right) / 2
mid if mid * mid >= num:
= mid - 1
right else:
= mid + 1
left ### NOTE this
return left * left == num
// java
// LC 367
public boolean isPerfectSquare(int num) {
if (num < 2) {
return true;
}
long left = 2;
long right = num / 2; // NOTE !!!, "long right = num;" is OK as well
long x;
long guessSquared;
while (left <= right) {
= (left + right) / 2;
x = x * x;
guessSquared if (guessSquared == num) {
return true;
}
if (guessSquared > num) {
= x - 1;
right } else {
= x + 1;
left }
}
return false;
}
# LC 209 Minimum Size Subarray Sum
### NOTE : there is also sliding window approach
# V1'
# http://bookshadow.com/weblog/2015/05/12/leetcode-minimum-size-subarray-sum/
# IDEA : BINARY SEARCH
class Solution:
def minSubArrayLen(self, s, nums):
= len(nums)
size = 0, size
left, right = 0
bestAns while left <= right:
= (left + right) / 2
mid if self.solve(mid, s, nums):
= mid
bestAns = mid - 1
right else:
= mid + 1
left return bestAns
def solve(self, l, s, nums):
= 0
sums for x in range(len(nums)):
+= nums[x]
sums if x >= l:
-= nums[x - l]
sums if sums >= s:
return True
return False
# LC 278
# V0
# IDEA : binary search
class Solution(object):
def firstBadVersion(self, n):
= 1
left = n
right while right > left + 1:
= (left + right)//2
mid if SVNRepo.isBadVersion(mid):
= mid
end else:
= mid
left if SVNRepo.isBadVersion(left):
return left
return right
# LC 035 Search Insert Position
# V1'
# https://blog.csdn.net/fuxuemingzhu/article/details/70738108
class Solution(object):
def searchInsert(self, nums, target):
= len(nums)
N = 0, N #[left, right)
left, right while left < right:
= left + (right - left) / 2
mid if nums[mid] == target:
return mid
elif nums[mid] > target:
= mid
right else:
= mid + 1
left return left
# LC 1011
# V1
# IDEA : BINARY SEARCH
# https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/discuss/390359/Simple-Python-Binary-Search
# V0
# IDEA : BINARY SEARCH
class Solution(object):
def shipWithinDays(self, weights, D):
"""
NOTE !!!
-> for this help func,
-> we ONLY need to check weights can split by offered max_wgt
-> so the return val is boolean (True or False)
"""
# help func
def cannot_split(weights, D, max_wgt):
= 0
s = 1
days for w in weights:
+= w
s if s > max_wgt:
= w
s += 1
days return days > D
"""
NOTE this !!!
-> for l, we use max(weights)
-> for r, we use sum(weights)
"""
= max(weights)
l = sum(weights)
r while l <= r:
= l + (r - l) // 2
mid if cannot_split(weights, D, mid):
= mid + 1
l else:
= mid - 1
r return l
# LC 410 Split Array Largest Sum [Hard]
// java
// LC 875
// V0
// IDEA : BINARY SEARCH (close boundary)
/**
* KEY !!!!
*
* -> When r < l, it means the `smallest` valid eating speed is l
*
*/
public int minEatingSpeed(int[] piles, int h) {
if (piles.length == 0 || piles.equals(null)){
return 0;
}
int l = 1; //Arrays.stream(piles).min().getAsInt();
int r = Arrays.stream(piles).max().getAsInt();
while (r >= l){
int mid = (l + r) / 2;
int _hour = getCompleteTime(piles, mid);
if (_hour <= h){
= mid - 1;
r }else{
= mid + 1;
l }
}
return l;
}
# LC 658. Find K Closest Elements
# V1'
# https://blog.csdn.net/fuxuemingzhu/article/details/82968136
# IDEA : TWO POINTERS
class Solution(object):
def findClosestElements(self, arr, k, x):
# since the array already sorted, arr[-1] must be the biggest one,
# while arr[0] is the smallest one
# so if the distance within arr[-1], x > arr[0], x
# then remove the arr[-1] since we want to keep k elements with smaller distance,
# and vice versa (remove arr[0])
while len(arr) > k:
if x - arr[0] <= arr[-1] - x:
arr.pop()else:
0)
arr.pop(return arr
# LC 069 Sqrt(x)
# V0
# IDEA : binary search
class Solution(object):
def mySqrt(self, x):
# edge case
if not x or x <= 0:
return 0
if x == 1:
return 1
= 0
l = x-1
r while r >= l:
= l + (r-l)//2
mid #print ("l = " + str(l) + " r = " + str(r) + " mid = " + str(mid))
= mid** 2
sq if sq == x:
return mid
elif sq < x:
if (mid+1)**2 > x:
return mid
= mid + 1
l else:
= mid - 1
r
# V0
# IDEA : binary search
class Solution(object):
def mySqrt(self, num):
if num <= 1:
return num
= 0
l = num - 1
r while r >= l:
= l + (r - l) // 2
mid if mid * mid == num:
return mid
elif mid * mid > num:
= mid - 1
r else:
= mid + 1
l return l if l * l < num else l - 1
# 34. Find First and Last Position of Element in Sorted Array
# V0
# IDEA : BINARY SEARCH
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def search(x):
= 0, len(nums)
lo, hi while lo < hi:
= (lo + hi) // 2
mid if nums[mid] < x:
= mid+1
lo else:
= mid
hi return lo
= search(target)
lo = search(target+1)-1
hi
if lo <= hi:
return [lo, hi]
return [-1, -1]
// java
// LC 34
// V0
// IDEA: BINARY SEARCH (fixed by gpt)
public int[] searchRange(int[] nums, int target) {
int[] res = new int[]{-1, -1}; // Default result
if (nums == null || nums.length == 0) {
return res;
}
// Find the first occurrence of target
int left = findBound(nums, target, true);
if (left == -1) {
return res; // Target not found
}
// Find the last occurrence of target
int right = findBound(nums, target, false);
return new int[]{left, right};
}
private int findBound(int[] nums, int target, boolean isFirst) {
int l = 0, r = nums.length - 1;
int bound = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target) {
= mid;
bound if (isFirst) {
= mid - 1; // Keep searching left
r } else {
= mid + 1; // Keep searching right
l }
} else if (nums[mid] < target) {
= mid + 1;
l } else {
= mid - 1;
r }
}
return bound;
}
// java
// LC 74
// V1
// IDEA : BINARY SEARCH + FLATTEN MATRIX
// https://leetcode.com/problems/search-a-2d-matrix/editorial/
public boolean searchMatrix_2(int[][] matrix, int target) {
int m = matrix.length;
if (m == 0)
return false;
int n = matrix[0].length;
// binary search
/** NOTE !!! FLATTEN MATRIX */
int left = 0, right = m * n - 1;
int pivotIdx, pivotElement;
while (left <= right) {
= (left + right) / 2;
pivotIdx /** NOTE !!! TRICK HERE :
*
* pivotIdx / n : y index
* pivotIdx % n : x index
*/
= matrix[pivotIdx / n][pivotIdx % n];
pivotElement if (target == pivotElement)
return true;
else {
if (target < pivotElement)
= pivotIdx - 1;
right else
= pivotIdx + 1;
left }
}
return false;
}
// java
// LC 153
// V0
// IDEA : BINARY SEARCH (CLOSED BOUNDARY)
// https://youtu.be/nIVW4P8b1VA?si=AMhTJOUhDziBz-CV
/**
* NOTE !!!
*
* key : check current `mid point` is at `left part` or `right part`
* if `at left part`
* -> nums[l] ~ nums[mid] is in INCREASING order
* -> need to search `RIGHT part`, since right part is ALWAYS SMALLER then left part
*
* else, `at right part`
* -> need to search `LEFT part`
*/
public int findMin(int[] nums) {
int l = 0;
int r = nums.length - 1;
int res = nums[0];
/** NOTE !!! closed boundary */
while (l <= r) {
// edge case : is array already in increasing order (e.g. [1,2,3,4,5])
if (nums[l] < nums[r]) {
= Math.min(res, nums[l]);
res break;
}
int m = l + (r - l) / 2;
= Math.min(res, nums[m]);
res
// case 1) mid point is at `LEFT part`
// e.g. [3,4,5,1,2]
if (nums[m] >= nums[l]) {
= m + 1;
l }
// case 2) mid point is at `RIGHT part`
// e.g. [5,1,2,3,4]
else {
= m - 1;
r }
}
return res;
}
// java
// LC 34
public int[] searchRange_1(int[] nums, int target) {
int[] result = new int[2];
[0] = findFirst(nums, target);
result[1] = findLast(nums, target);
resultreturn result;
}
private int findFirst(int[] nums, int target) {
int idx = -1;
int start = 0;
int end = nums.length - 1;
while (start <= end) {
int mid = (start + end) / 2;
/** NOTE !!!
*
* 1) nums[mid] >= target (find right boundary)
* 2) we put equals condition below (nums[mid] == target)
*/
if (nums[mid] >= target) {
= mid - 1;
end } else {
= mid + 1;
start }
if (nums[mid] == target)
= mid;
idx }
return idx;
}
private int findLast(int[] nums, int target) {
int idx = -1;
int start = 0;
int end = nums.length - 1;
while (start <= end) {
int mid = (start + end) / 2;
/** NOTE !!!
*
* 1) nums[mid] <= target (find left boundary)
* 2) we put equals condition below (nums[mid] == target)
*/
if (nums[mid] <= target) {
= mid + 1;
start } else {
= mid - 1;
end }
if (nums[mid] == target)
= mid;
idx }
return idx;
}