labuladong_二分查找
零、二分查找框架
func binarySearch(nums []int, target int) int {
left := 0
right := len(nums) - 1 //注意
for left <= right { //注意
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
分析二分查找的一个技巧是:不要出现 else,而是把所有情况用 else if 写清楚,这样可以清楚地展现所有细节。本文都会使用 else if,旨在讲清楚,读者理解后可自行简化。
其中 ... 标记的部分,就是可能出现细节问题的地方,当你见到一个二分查找的代码时,首先注意这几个地方。后文用实例分析这些地方能有什么样的变化。
另外声明一下,计算 mid 时需要防止溢出,代码中 left + (right - left) / 2 就和 (left + right) / 2 的结果相同,但是有效防止了 left 和 right 太大直接相加导致溢出。
二、寻找左侧边界的二分搜索
完整代码如下:
func LeftBound2(nums []int, target int) int {
left := 0
right := len(nums) - 1 //注意
for left <= right { //注意
mid := left + (right-left)/2
if nums[mid] == target {
//收缩右侧边界
right = mid - 1
} else if nums[mid] < target {
//搜索区间变为 [mid+1, right]
left = mid + 1 //注意
} else if nums[mid] > target {
//搜索区间变为 [left, mid-1]
right = mid - 1
}
}
if left >= len(nums) || nums[left] != target {
return -1
}
return left
}
这样就和第一种二分搜索算法统一
三、寻找右侧边界的二分查找
类似寻找左侧边界的算法,这里也会提供两种写法,还是先写常见的左闭右开的写法,只有两处和搜索左侧边界不同,已标注:
func RightBound(nums []int, target int) int {
left := 0
right := len(nums) - 1 //注意
for left <= right { //注意
mid := left + (right-left)/2
if nums[mid] == target {
//收缩左侧边界
left = mid + 1
} else if nums[mid] < target {
//搜索区间变为 [mid+1, right]
left = mid + 1 //注意
} else if nums[mid] > target {
//搜索区间变为 [left, mid-1]
right = mid - 1
}
}
if right < 0 || nums[right] != target {
return -1
}
return right
return right }
1、为什么这个算法能够找到右侧边界?
答:类似地,关键点还是这里:
if nums[mid] == target {
//收缩左侧边界
left = mid + 1
}
当 nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。
当 target 比所有元素都小时,right 会被减到 -1,所以需要在最后防止越界:

java基础知识


浙公网安备 33010602011771号