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基础知识
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话