1.トピック
昇順で配置された整数配列numsと、ターゲット値targetが与えられます。配列内の指定されたターゲット値の開始位置と終了位置を見つけます。
ターゲット値targetが配列に存在しない場合は、[-1、-1]を返します。
上級:
- この問題を解決するためにO(log n)アルゴリズムを設計および実装できますか?
例1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
例2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
例3:
输入:nums = [], target = 0
输出:[-1,-1]
促す:
- 0 <= nums.length <= 105
- -109 <= nums [i] <= 109
- numsは減少しない配列です
- -109 <=ターゲット<= 109
二、解く
1.二分探索法
アイデア:
配列nums
内のすべての数値を並べ替えtarget
てウィンドウを形成します。ウィンドウの左右の境界インデックスはそれぞれleft
とでありright
、それぞれウィンドウの左右の最初の要素に対応していることに注意してください。
target
発生のデジタル数は次のように変換できます。左マージンに二分法が見つかりleft
、有界でありright
、デジタルtarget
数を簡単に取得できright-left-1
ます。
アルゴリズム分析:
- 初期化:左側の境界
i = 0
、境界ありj = len(nums)-1
。 - サイクル二分法:閉区間に
[i, j]
要素がないときに飛び出す:
2.1。中点を計算するm = (i+j)/2
(切り捨て)
2.2。の場合nums[m]<target
、ターゲットは閉区間[m+1, j]
にあるので、実行しi = m+1
ます;
2.3。の場合nums[m]>target
、ターゲットはにあります閉区間な[i, m-1]
ので、実行しj = m-1
ます;
2.4。の場合nums[m]=target
、右の境界は区間内に[m+1, j]
あり、左の境界は閉区間内にあります[i, m-1]
。したがって、次の2つのケースに分けられ
ます。[1]。右の境界が右に見つかった場合は、i = m+1
;(ジャンプアウトするときは右の境界を指します)
[2]を実行します。左の境界が左にある場合は、次にj = m-1
;を実行します(jポイントを左の境界にジャンプアウトする場合) - 戻り値。二分法を2回適用し、それぞれ
right
合計を見つけて、left
最後にを返しright-left-1
ます。
コード-バージョン1:
class Solution {
public int search(int[] nums, int target) {
// 搜索右边界 right
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] <= target) i = m + 1;
else j = m - 1;
}
int right = i;
// 若数组中无 target ,则提前返回
if(j >= 0 && nums[j] != target) return 0;
// 搜索左边界 right
i = 0; j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] < target) i = m + 1;
else j = m - 1;
}
int left = j;
return right - left - 1;
}
}
時間計算量: O(ログ)O(ログ)O (l o g n )
空間の複雑さ: O(1)O(1)O (1 )
上記のコードは読みにくく、次のバージョンに最適化できます。
コード-バージョン2:
class Solution {
public int search(int[] nums, int target) {
return helper1(nums, target) - helper2(nums, target) - 1;
}
public int helper1(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] <= target) left = mid + 1;
else right = mid - 1;
}
return left;
}
public int helper2(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
return right;
}
}
上記のコードは非常に冗長であり、さらに最適化できます。
コード-バージョン3:
class Solution {
public int search(int[] nums, int target) {
return helper(nums, target) - helper(nums, target - 1);
}
int helper(int[] nums, int tar) {
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] <= tar) i = m + 1;
else j = m - 1;
}
return i;
}
}
時間計算量: O(n)O(n)O (n )
スペースの複雑さ: O(n)O(n)O (n )