二分查找的难点在于到底要给 mid 加⼀还是减⼀,while ⾥到底⽤ <= 还是 < 。
⼀、寻找⼀个数(基本的⼆分搜索)
示例代码1:
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1; // 注意
else if (nums[mid] > target)
right = mid - 1; // 注意
}
return -1;
}
示例代码2:【python】
def binary_search(lst, target):
left = 0
right = len(lst) - 1
while left <= right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
return mid
elif lst[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
lst = [1, 2, 2, 2, 3]
target = 2
ret = binary_search(lst, target)
print(ret)
1、为什么 while 循环的条件中是 <=,⽽不是 <?
⼆、寻找左侧边界的⼆分搜索
示例代码1:
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0;
int right = nums.length; // 注意
while (left < right) { // 注意
int mid = (left + right) / 2;
if (nums[mid] == target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid; // 注意
}
}return left;
}
def binary_search(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst)
while left < right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
right = mid
elif lst[mid] < target:
left = mid + 1
else:
right = mid
return left
lst = [1, 2, 2, 2, 3]
target = 2
ret = binary_search(lst, target)
print(ret)
while (left < right) {
//...
}// target ⽐所有数都⼤
if (left == nums.length) return -1;
// 类似之前算法的处理⽅式
return nums[left] == target ? left : -1;
将上述示例2python代码整体修改后为:
def binary_search(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst)
while left < right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
right = mid
elif lst[mid] < target:
left = mid + 1
else:
right = mid
# target比所有数都大
if left == len(lst):
return -1
# 类似之前的代码处理
return left if lst[left] == target else -1
lst = [1, 2, 2, 2, 3]
target = 5
ret = binary_search(lst, target)
print(ret)
if (nums[mid] == target)
right = mid;
示例代码:
def binary_search(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst)
while left < right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
right = mid
elif lst[mid] < target:
left = mid + 1
else:
right = mid
# target比所有数都大
if right == len(lst):
return -1
# 类似之前的代码处理
return right if lst[right] == target else -1
lst = [1, 2, 2, 2, 3]
target = 2
ret = binary_search(lst, target)
print(ret)
int left_bound(int[] nums, int target) {
// 搜索区间为 [left, right]
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
// if else ...
}
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] == target) {
// 收缩右侧边界
right = mid - 1;
}
if (left >= nums.length || nums[left] != target)
return -1;
return left;
def binary_search(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst) - 1
while left <= right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
# 收缩右边界
right = mid - 1
elif lst[mid] < target:
# 搜索区间变为[mid + 1, right]
left = mid + 1
else:
# 搜索区间变为[left, mid - 1]
right = mid - 1
# 检查出界情况
if left >= len(lst) or lst[left] != target:
return -1
return left
lst = [1, 2, 2, 2, 3]
target = 2
ret = binary_search(lst, target)
print(ret)
三、寻找右侧边界的⼆分查找
int right_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
left = mid + 1; // 注意
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
return left - 1; // 注意
}
def right_bound(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst)
while left < right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
left = mid + 1
elif lst[mid] < target:
left = mid + 1
else:
right = mid
return left - 1
lst = [1, 2, 2, 2, 3]
target = 2
ret = right_bound(lst, target)
print(ret)
if (nums[mid] == target) {
left = mid + 1;
if (nums[mid] == target) {
left = mid + 1;
// 这样想: mid = left - 1
while (left < right) {
// ...
}
if (left == 0) return -1;
return nums[left-1] == target ? (left-1) : -1;
完整代码如下:
def right_bound(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst)
while left < right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
left = mid + 1
elif lst[mid] < target:
left = mid + 1
else:
right = mid
if left == 0:
return -1
return left - 1 if lst[left-1] == target else -1
lst = [1, 2, 2, 2, 3]
target = 5
ret = right_bound(lst, target)
print(ret)
4、是否也可以把这个算法的「搜索区间」也统⼀成两端都闭的形式呢?
示例代码: 【python】
def right_bound(lst, target):
if len(lst) == 0:
return -1
left = 0
right = len(lst) - 1
while left <= right:
mid = int(left + (right - left) / 2)
if lst[mid] == target:
# 这⾥改成收缩左侧边界即可
left = mid + 1
elif lst[mid] < target:
left = mid + 1
else:
right = mid - 1
# 这⾥改为检查right越界的情况,⻅下图
if left < 0 or lst[right] != target:
return -1
return right
lst = [1, 2, 2, 2, 3]
target = 2
ret = right_bound(lst, target)
print(ret)