口诀
- 通用 left := 0,right := len(nums) - 1 ,for left <= right
- 找左边 等于不返回,收缩 right = mid - 1,返回 left
- 找右边 等于不返回,收缩 left = mid + 1, 返回 right
- 找值 等于返回mid
实战
1. 找值
func search(nums []int, target int) int {
left := 0
right := len(nums) - 1
for left <= right {
mid := (left + right) / 2
if nums[mid] == target {
return mid
}
if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
2. 找左
func findLeft(nums []int, target int) int {
left := 0
right := len(nums) - 1
find := false
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
right = mid - 1 // 收缩右边界
find = true
} else if nums[mid] < target {
left = mid + 1
} else if nums[mid] > target {
right = mid - 1
}
}
if find {
return left
}
return -1
}
3. 找右
func findRight(nums []int, target int) int {
left := 0
right := len(nums) - 1
find := false
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
left = mid + 1 // 收缩左边界
find = true
} else if nums[mid] < target {
left = mid + 1
} else if nums[mid] > target {
right = mid - 1
}
}
if find {
return right
}
return -1
}
-
你要找的是 target 的最右位置。
-
当
nums[mid] == target时:- 说明
mid这个位置是合法解,但可能右边还有更靠后的target。 - 所以我们把 左边界往右移 (
left = mid + 1) ,继续尝试。 最终循环结束时:
- 说明
-
right停在最后一个等于 target 的位置。 -
left停在 target 的右侧第一个不等于 target 的位置。
示例解析
给定数组:
nums = [1, 3, 3, 4, 5]
target = 3
搜索过程
-
第一次循环
left = 0, right = 4mid = 2nums[mid] = 3 == target- 操作:
left = mid + 1 = 3 - 状态:
left = 3, right = 4
-
第二次循环
left = 3, right = 4mid = 3nums[mid] = 4 > target- 操作:
right = mid - 1 = 3 - 状态:
left = 3, right = 3
-
第三次循环
left = 3, right = 3mid = 3nums[mid] = 4 > target- 操作:
right = mid - 1 = 2 - 状态:
left = 3, right = 2
-
循环结束
- 此时
left > right,循环退出。
- 此时
right = 2- 这就是 最后一个等于 target 的位置。
为了避免找不到目标时误返回,可以在 nums[mid] == target 时设置一个 find = true,
最终根据 find 判断是否返回 -1。