一、关于二分:
二分解决的题目类型为:输入若干数(最好是已经排序好的数,也就是有单调性),查找指定的某一个数在这些数中的位置(包括起始位置和终止位置)。
二分的思想是找好边界。左端点l右端点r,中间点mid=(l+r)>>1,每次判断中间值是否满足性质,即mid和x(指定查找的一个数)的关系:如果mid比x大,说明x在mid的左边,也就是在区间[l,mid]中,把r换成了mid。如果mid比x小,说明x在mid的右边,也就是在区间[mid,r]中 。
二、看个题目了解一下
给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回“-1 -1”。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1~10000范围内),表示完整数组。
接下来q行,每行包含一个整数k,表示一个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回“-1 -1”。
数据范围
1≤n≤100000
1≤n≤100000
1≤q≤10000
1≤q≤10000
1≤k≤10000
1≤k≤10000
样例
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
用二分去查找元素要求数组的有序性或者拥有类似于有序的性质,对本题而言,一个包含重复元素的有序序列,要求输出某元素出现的起始位置和终止位置,翻译一下就是:在数组中查找某元素,找不到就输出−1−1,找到了就输出不小于该元素的最小位置和不大于该元素的最大位置。所以,需要写两个二分,一个需要找到>=x的第一个数,另一个需要找到<=x的最后一个数。
查找不小于x的位置(起始位置):
int l = 0, r = n - 1;//左右边界定义
while (l < r) {
int mid = l + r >> 1;
if (a[mid] < x) l = mid + 1;//如果中间的元素比x要小,则x肯定在靠右边界的区域内,所以小于mid的区域不可能有x。
else r = mid;如果x小于中间的元素,那么肯定是在区域的左半段
}
当查找结束时,l与r相遇,l所在元素若是x则一定是x出现最小位置,因为l左边的元素必然都小于x。
查找不大于x的位置(终止位置):
int l1 = l, r1 = n;//l为左边界,n为输入的数字个数
while (l1 + 1 < r1) {
int mid = l1 + r1 >> 1;
if (a[mid] <= x) l1 = mid; //x如果大于中间数字,肯定是在靠右边界的区域,所以左边界就变成mid
else r1 = mid; //反之有边界则会变成mid
}
总的代码如下:
#include <iostream>
using namespace std;
const int maxn = 100005;
int n, q, x, a[maxn];
int main() {
scanf("%d%d", &n, &q);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
while (q--) {
scanf("%d", &x);
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] < x) l = mid + 1;//注意是mid+1,因为是小于而不是小于等于
else r = mid;
}//判断不小于x的位置也就是起始位置
if (a[l] != x) {
printf("-1 -1\n");
continue;
}
int l1 = l, r1 = n;
while (l1 + 1 < r1) {
int mid = l1 + r1 >> 1;
if (a[mid] <= x) l1 = mid;
else r1 = mid;
}//判断不大于x的位置也就是终止位置
printf("%d %d\n", l, l1);
}
return 0;
}
本文解析了如何使用二分查找算法解决一个有序数组中查找特定元素及其起始和终止位置的问题,涉及查找不小于和不大于目标值的区间。通过实例展示了如何在Python中实现这个过程,并给出了样例输入和输出结果。

被折叠的 条评论
为什么被折叠?



