問題の説明:
昇順に配列された整数の配列を指定してnums
、目標値target
。アレイ内の所与の標的の開始および終了位置を特定します。
アルゴリズムのあなたの時間複雑でなければなりません O(ログのN-)レベル。
何のターゲット配列とリターンが存在しない場合 [-1, -1]
。
例1:
入力: NUMS = [ 5,7,7,8,8,10]
8 =ターゲット。
出力: [3,4]
例2:
入力: NUMS = [ 5,7,7,8,8,10]
6 =ターゲット。
出力: [-1、-1]
基本的な考え方:
非常に単純な、見つけることである一番左のターゲットを、見つけることですターゲット右端のを。
ここでは、二分思考の使用は、これはソート配列に1つの単純な要素を見つける前半分ではありません。
彼はこれに周囲の要素、に従ってランク付けする直接の要素は左と右の点を決定、我々は半ば要素の判断をあきらめるかもしれません。
これは、要素を取り巻く環境で左右の点から、一定周期、私たちを介している互いの中にバンプする左右、その位置の結果です。
(ただし、そこに問題があり、それはある左右の隣接する時間が無限ループを引き起こす可能性がある場合は。
普通によると、半ば= +左(右-左 )/ 2 、我々は最終的に同じ場所に半ば、常にポイントを残し、
これを防ぐために、我々は完全に組み込むことができます=左ミッド+(右-左)/ 2 + 1。 、半ば意識的に右へ)
ACコード:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.size() == 0) return {-1, -1};
// 找到第一个该数字出现的位置
int i = 0, j = nums.size() - 1;
while (i < j) {
// 为什么这里不用加1?
int m = i + (j - i) / 2;
// 这种方式j始终在向target靠
if (nums[m] >= target) j = m; // j是后面的指针,这里是往前靠了
else i = m + 1; // 感觉挺巧妙的,这里i也自动向target靠了
// 注意这里i指向的是不超过nums[m]的最大元素
}
int left;
if (target == nums[j]) left = j;
else return {-1, -1};
// 找到最后一个该数字出现的位置
i = 0, j = nums.size() - 1;
while (i < j) {
int m = i + (j - i) / 2 + 1;
if (nums[m] > target) j = m - 1;
else i = m; // i是前面的指针,这里往后靠了
}
int right = i;
return {left, right};
}
};
その他の経験:
先に与えられたテンプレートは、終了条件は、+ 1 <右を残しています。そうする必要は全くありません。
最後に、左と右の追加の書き込み処理があります。
正しい言葉遣いは私たちの目的を達成するために、半ばを変更することです。