一些对二分查找的理解, 主要是关于边界问题的思考
二分框架
在二分查找中,虽然有许多需要注意到细节,但是总体框架还是比较统一的,所以先说一下大框架
func binary_search(nums []int, target int) int {
left, right := ... // 初始化两边的参数 决定了是开区间还是闭区间
// 在不同的区间类型选择里 使用不同的operator
for left operator right {
mid := left + (right - left)/2
if nums[mid] operator target {
change left point
} else {
change right point
}
}
return left or right
}
总结一下,在经典的二分中,需要改变的点就是:
- 使用什么样的区间
- [left, right] :左闭右闭区间
- [left, right) :左闭右开区间
- (left, right) :左开右开区间
- 需要什么样子的结果
<= < > >=
- 如何更新左右指针
- left = mid or left = mid - 1 or …
- right = mid or right = mid + 1 or …
- 根据所需的结果选择返回left还是right
- return left
- return right
对于上面的伪代码,有两点需要说明:
- 在伪代码中使用了类似 golang 的语法,for 在语言中的作用与 c/c++ 中的 while 一样
- 更新 mid 的操作,其实等同于
mid := (left + right)/2
,但是如伪代码所写,可以防止溢出
具体理解(以[left, right]为例)
// [left, right]
// [left, len(nums)] 是符合更新right的那个if条件的集合
// [0, right] 是符合更新left的那个if条件的集合
func binary_search_1(nums []int, target int) int {
left, right := 0, len(nums)-1 // [left, right]
for left <= right {
mid := left + (right-left)/2
if nums[mid] < target { // or <=,>,>=
left = mid + 1
} else {
right = mid - 1
}
}
return left // or right
}
其他区间 code snippets
[left, right)
// [left, right)
// [left, len(nums)] 是符合更新right的那个if条件的集合
// [0, right) 是符合更新left的那个if条件的集合
func binary_search_2(nums []int, target int) int {
left, right := 0, len(nums) // [left, right)
for left < right {
mid := left + (right-left)/2
if nums[mid] < target { // or <=,>,>=
left = mid + 1
} else {
right = mid
}
}
return left // or right
}
(left, right)
// (left, rihgt)
// (left, len(nums)] 是符合更新right的那个if条件的集合
// [0, right) 是符合更新left的那个if条件的集合
func binary_search_3(nums []int, target int) int {
left, right := -1, len(nums) //(left, rihgt)
for left+1 < right {
mid := left + (right-left)/2
if nums[mid] < target { // or <=,>,>=
left = mid
} else {
right = mid
}
}
return left // or right
}
一些转换
对于整数而言
indexOf(>=target)
indexOf(>target)
(相当于 indexOf(>=target)+1
)
indexOf(<target)
(相当于 indexOf(>=target)-1
)
indexOf(<=target)
(相当于 indexOf(>target)-1
)