寻找有序序列中第一个满足某条件的元素的位置
- 二分的区间是答案可能出现的区间,而不是(不一定是)有序序列的区间,比如说,对于“寻找序列中第一个大于等于
x
的元素的位置(lower_bound
)”这个问题,如果给定有序序列的范围是,那么考虑到x
有可能比这个序列中所有的数都要大,实际上答案的范围是。 - 不管是什么形式的区间,
while
循环进行的条件永远都是是否找到了唯一的位置,也就是待二分的区间是否可以夹出唯一的位置。 - 给
right
或left
赋值,实际上是选择左区间或者右区间,然后继续二分。要注意,赋值之后区间的形式要保持统一,比如一开始区间是左闭右开,那么赋值之后仍应该是左闭右开的。
这类问题有几个常见的算法:
lower_bound
寻找第一个大于等于x的元素的位置upper_bound
寻找第一个大于x的元素的位置
这两个函数在<algorighm>
中均有实现,且参数区间范围均是左闭右开,也就是说,答案的范围相当于同一个区间的左闭右闭。
// 寻找有序序列第一个满足某条件的元素的位置
int solve (int left, int right) {
int mid;
while (没有找到唯一位置) {
mid = (left + right) / 2 // 取中点
// mid = left + (right - left) / 2 // 等价写法避免溢出
if (A[mid]满足条件) { // 找第一个满足条件的位置
给 right 赋值
} else {
给 left 赋值
}
}
return 唯一的位置
}
寻找序列中是否存在满足某条件的元素
- 这个问题完整的描述是“寻找序列中是否存在满足某条件的元素,如果存在则返回元素的位置,否则返回-1”。
- 对于这类问题,序列中的元素最好不要重复,否则答案位置不唯一,这时应该使用
lower_bound
和upper_bound
找到答案的上下边界。 - 如果只是想要知道是否存在,可以直接使用
<algorithm>
中的binary_search
。在已知存在的情况下,如果还想进一步知道位置,可以使用lower_bound
,也就是说,同时使用标准库函数binary_search
和lower_bound
,可以达到与下面的代码一样的目的,而且查找到的一定是第一个可能的位置。
int binary_search (int left, int right) {
int mid;
while (没有找到唯一位置) {
mid = (left + right) / 2 // 取中点
// mid = left + (right - left) / 2 // 等价写法避免溢出
if(A[mid]满足条件) return mid;
if (待找元素在左子区间) {
给 right 赋值
} else {
给 left 赋值
}
}
return -1; // 查找失败
}
来源:https://blog.csdn.net/Exupery_/article/details/100837066