二分查找边界
查找左边界
问:给定一个长度为 的有序数组 nums ,其中可能包含重复元素。请返回数组中最左一个元素 target 的索引。若数组中不包含该元素,则返回 -1。
用二分查找插入点的方法,搜索完成后i指向最左一个target,因此查找插入点本质上是在查找最左一个target的索引
数组中可能不包含 target ,这种情况可能导致以下两种结果
- 插入点的索引i越界
- 元素nums[i]与target不相等
如果遇到上述情况,可直接返回-1
/* 二分查找最左一个 target */
func binarySearchLeftEdge(nums []int, target int) int {
//等价于查找target的插入点
i := binarySearchInsertionSimple(nums, target)
//未找到target返回-1
if i == len(nums) || nums[i] != target {
return -1
}
//找到target
return i
}
/* 二分查找插入点(无重复元素) */
func binarySearchInsertionSimple(nums []int, target int) int {
// 初始化双闭区间 [0, n-1]
i, j := 0, len(nums)-1
for i <= j {
// 计算中点索引 m
m := i + (j-i)/2
if nums[m] < target {
// target 在区间 [m+1, j] 中
i = m + 1
} else if nums[m] > target {
// target 在区间 [i, m-1] 中
j = m - 1
} else {
// 找到 target ,返回插入点 m
return m
}
}
// 未找到 target ,返回插入点 i
return i
}
查找右边界
那么如何查找最右一个 target 呢?最直接的方式是修改代码,替换在 nums[m] == target 情况下的指针收缩操作。
我有一种更简单的方法,就是复用查找左边界
- 将查找最右一个
target转化为查找最左一个target + 1
查找完成后,指针i指向最左一个 target + 1(如果存在),而j指向最右一个 target ,因此返回 即可。
/* 二分查找最右一个 target */
func binarySearchRightEdge(nums []int, target int) int {
// 转化为查找最左一个 target + 1
i := binarySearchInsertion(nums, target+1)
// j 指向最右一个 target ,i 指向首个大于 target 的元素
j := i - 1
// 未找到 target ,返回 -1
if j == -1 || nums[j] != target {
return -1
}
// 找到 target ,返回索引 j
return j
}