题目描述
给定一个按照升序排列的长度为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
优化二分查找跳过重复元素
此题采用正常的二分查找会超时,但我们可以注意到,此题有很多重复的数字例如(111223333)
我们二分查找时左边查找到第一个1的时候就可以跳过其余的1
可以通过一个数组来实现记录重复数字的个数,在二分查找时可以直接跳过
java代码
package 蓝桥;
import java.util.Scanner;
/******
*
* @author genji
* 此题采用正常的二分查找会超时,但我们可以注意到,此题有很多重复的数字例如(111223333)
* 我们二分查找时左边查找到第一个1的时候就可以跳过其余的1
* 可以通过一个数组来实现记录重复数字的个数,在二分查找时可以直接跳过
*/
public class 数的范围_789 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt(),m=scanner.nextInt();
int[] a=new int[n];
int[] b=new int[n];//b中存放重复数字的数量,因为下面采用二分查找所以有前后两个标志
//例如a 中的数为1,1,1,1,1,2,2,2,2,3,3,4,4,4,4,5
//则b中的数为 5,0,0,0,5,4,0,0,4,2,2,4,0,0,4,1
//使其在查找时跳过重复的数字
int c=0,i;
for (i = 0; i < n; i++) {
a[i]=scanner.nextInt();
if (i==0||a[i]==a[i-1]) {
c++; //如果是第一个数或者和前面数字相同的数则记录相同数字的个数
}
else if (a[i]!=a[i-1]) {
b[i-1]=c;
b[i-c]=c; //将相同数字个数赋到b数组相同下标的头和尾
c=1;
}
}
b[i-1]=c;
b[i-c]=c;
for (i = 0; i < b.length; i++) {
System.out.println(b[i]);
}
while (m-->0) {
int l=0,r=n-1;
int q=scanner.nextInt();
while (l<r) {
if (a[l]!=q) {
l+=b[l];// 跳过重复的数字
}
if (a[r]!=q) {
r-=b[r];// 跳过重复的数字
}
if (a[r]==q&&a[l]==q) {
System.out.println(l+" "+r);
break;
}
}
if(a[l]!=q&&a[r]!=q)
{
System.out.println(-1+" "+-1);
}
}
}
}