前置知识:无
建议:1 会的同学可以跳过,2、3、4 不要跳过
题目
1.在有序数组中确定 num 存在还是不存在
要明确 right
的含义,常见的有:
[left, right]
[left, right)
// 保证 arr 有序
func exist(arr []int, target int) bool {
n := len(arr)
left, right := 0, n-1 // [left, right]
for left <= right { // 为什么加 =?left、right 指向同一个位置,这个数字也要考虑
mid := left + (right-left)>>1
if arr[mid] > target {
right = mid - 1
} else if arr[mid] < target {
left = mid + 1
} else {
return true
}
}
return false
}
// 保证 arr 有序
func exist(arr []int, target int) bool {
n := len(arr)
left, right := 0, n // [left, right),right 指向有效位置的下一位
for left < right { // 为什么没有 =?left、right 指向同一个位置,right 位置无效,这个数字无效
mid := left + (right-left)>>1
if arr[mid] > target {
right = mid // 为什么没有 -1? right 指向有效位置的下一位, mid已经判断过了——无效
} else if arr[mid] < target {
left = mid + 1
} else {
return true
}
}
return false
}
写代码时要时刻明确 right
的含义,一会儿 ,一会儿 ,一会儿加一,一会儿不加,搞不清楚可能会导致错误的答案或死循环
2.在有序数组中找>=num的最左位置
arr[mid] == target
时,记录答案,但它不一定是最左的位置,所以要继续向左侧二分
// 保证 arr 升序
func GreatEqualMostLeftIdx(arr []int, target int) int {
ans := -1
n := len(arr)
left, right := 0, n // [left, right)
for left < right {
mid := left + (right-left)>>1
if arr[mid] < target {
left = mid + 1
} else {
ans = mid
right = mid
}
}
return ans
}
如果是找 <= num
的最左位置呢?
直接看 0
位置,如果 0
位置 <=num
,则最左位置是 0
;否则是 -1
为啥?因为数组升序
如果是找 > num
的最左位置呢?
3.在有序数组中找<=num 的最右位置
arr[mid] == target
时,记录答案,但它不一定是最右的位置,所以要继续向右侧二分
// 保证 arr 升序
func LessEqualMostRightIdx(arr []int, target int) int {
ans := -1
n := len(arr)
left, right := 0, n // [left, right)
for left < right {
mid := left + (right-left)>>1
if arr[mid] > target {
right = mid
} else {
ans = mid
left = mid + 1
}
}
return ans
}
如果是找 < num
的最右位置呢?
4.二分搜索不一定发生在有序数组上
比如 寻找峰值问题
取 mid
mid = (left + right) / 2
其中 left + right
可能会溢出,改写为:left + (right - left) / 2
二分搜索
如果数组长度为 n,那么二分搜索搜索次数是 次,下节课讲时间复杂度,二分搜索时间复杂度 O(logn)
二分搜索不一定发生在有序数组上,只要能确定可以留下(排除)一侧数据即可,即:具有“单调性”
二分答案法
“二分答案法”这个非常重要的算法,很秀很厉害,将在【必备】课程里继续