質問の詳細
昇順で配置された整数配列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]、ターゲット= 6
出力:[-1、-1]
この質問を書く目的は、二分法の能力を行使することです。したがって、結局のところ、logNをさらに書く必要があります。
第二に、この質問のサブ問題は非常に役立つ質問になる可能性があります。
分析と回答
質問のアイデアはこのように書くことができます
二分查找:
如果nums[mid]>target :
则target在另一边
如果nums[mid]<target :
则target在另一边
如果nums[mid]==target:
则找到了目标值,于是便来寻找其第一个与第二个
在左区间find第一个target
在右区间find最后的target
return
return
ここでは、バイナリ検索を使用して、左右の間隔で最初と最後のターゲットを検索するため、合計はlogNになります。
私の書き方の利点は、最初のものを見つけて最後のものを直接見つけるlogNよりも少し小さいことですが、大きさの順序は同じです。
最初と最後のものを見つけるアイデアについては、コードを直接見てください
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;//未找到
}
ここで最後に見つかったものは、上記のアイデアと一致していますが、少し変更するだけです。
実際、上記は以下の質問に使えると思います。結局、logNは最初か最後かを見つけますが、それでも練習する必要があります。
全体的なコードは次のとおりです。
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};
}