本题的考点在于二分查找。二分查找的主要思想就是,针对一个有序的数据集合,每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0。
举个例子:
对于数组[8, 11, 19, 23, 27, 33, 45, 55, 67, 98],查找元素19
除此之外,还有类似于查找最后一个值等于给定值的元素,查找第一个大于等于给定值的元素,查找最后一个小于等于给定值的元素。
这里推荐两个二分查找的模板,掌握之后可以很轻易地解决二分的问题: www.acwing.com/blog/conten…
针对二分查找,我这里需要重点强调一点,就是其本质并不是单调性,而是找到满足某种性质的边界。例如下图所示,对于一个数据集合来说,红线部分和绿线部分都满足某种独特的性质,那么通过二分,我们就可以找到这两部分的边界。
#include <cstdio>
const int N = 100010;
int arr[N];
int main() {
int n, q;
scanf("%d %d", &n, &q);
for(int i = 0; i < n; ++i) scanf("%d", &arr[i]);
while(q--) {
int i = 0, j = n - 1, k;
scanf("%d", &k);
while(i < j) {
int mid = i + ((j - i) >> 1);
if(arr[mid] >= k) j = mid;
else i = mid + 1;
}
if(arr[i] != k) {
printf("-1 -1\n");
} else {
printf("%d ", i);
i = 0, j = n - 1;
while(i < j) {
int mid = i + ((j - i + 1) >> 1);
if(arr[mid] <= k) i = mid;
else j = mid - 1;
}
printf("%d\n", i);
}
}
return 0;
}
对于模板中求解mid时,为什么有时候需要+1?
因为在第一种情况下,i一直在更新,而j则有可能会保持不变,但是前提条件是i < j,所有当i和j相差1的时候,最终还是会落在i上,所以更新会继续,不会出现死循环;而第二种情况下,更新的是j,i有可能会保持不变,当i和j相差1的时候,最终会落在i上,导致死循环的出现,因此我们需要在i和j相差为1的时候,落在j上,这样更新就会继续。
参考 王争 《数据结构与算法之美》