题目详情
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
写这题的目的,是为了锻炼自己的二分能力,二分还是要多写啊,毕竟logN。
其次,这题的子问题可以是非常有用的问题。
分析与解答
该题的思路可以写成这样
二分查找:
如果nums[mid]>target :
则target在另一边
如果nums[mid]<target :
则target在另一边
如果nums[mid]==target:
则找到了目标值,于是便来寻找其第一个与第二个
在左区间find第一个target
在右区间find最后的target
return
return
这里,在左右区间find第一个与最后一个target都是用的二分查找,所以总的都是logN。
我这种的写法的好处,是比直接上来就find第一个,find最后一个,的logN的N要小一点,不过数量级还是一致的。
关于find第一个和最后一个的思路直接看代码
public int findFirstTarget(int[] nums,int target){
int l = 0, r = nums.length-1;
int mid = l + ((r-l)>>1);
while(l<=r){
if(nums[mid]>target){
r = mid - 1;
}
else if(nums[mid]<target){
l = mid + 1;
}
else{
if(mid==l || nums[mid-1]!=target){
return mid;
}
r = mid - 1;
}
mid = l + ((r-l)>>1);
}
return -1;//未找到
}
这里find最后一个和上述思路一致,稍作修改即可。
以上的其实感觉后面做题都可以用的到,毕竟logN查找到第一个or最后一个,还是有必要练习的。
整体的代码是:
public int[] searchRange(int[] nums, int target) {
//思路这样
//首先二分查找,修改l与r
//一旦mid找到,则两边分别找第一个target和最后一个target
int l=0, r=nums.length-1;
while(l<=r){
int mid = l+ ((r-l)>>1);
if(nums[mid]<target){
l = mid+1;
}
else if(nums[mid]>target){
r = mid-1;
}
else {
//target==nums[mid],则左区间寻找第一个target,右区间寻找最后一个target
//都是二分法查找
int ll = l, lr = mid;
int lmid = ll + ((lr-ll)>>1);
while(ll<=lr){
if(nums[lmid]<target){
ll = lmid +1;
}
else{
if(lmid==ll || nums[lmid-1]!=target){
break;
}
lr = lmid -1;
}
lmid = ll + ((lr-ll)>>1);
}
int rl = mid, rr = r;
int rmid = rl + ((rr-rl)>>1);
while(rl<=rr){
if(nums[rmid]>target){
rr = rmid - 1;
}
else{
if(rmid==rr || nums[rmid+1]!=target){
break;
}
rl = rmid + 1;
}
rmid = rl + ((rr-rl)>>1);
}
return new int[]{
lmid,rmid};
}
}
return new int[]{
-1,-1};
}