LeetCodeは、並べ替えられた配列内の要素の最初と最後の位置を検索します(34の質問)

LeetCodeは、ソートされた配列内の要素の最初と最後の位置を検索します

@author:Jingdai
@date:2020.11.09

トピックの説明(34の質問)

昇順の整数配列numsとターゲット値が与えられますtarget配列内の指定されたターゲット値の開始位置と終了位置を見つけます。

アルゴリズムの時間計算量はO(log n)レベルである必要があります。

ターゲット値が配列に存在しない場合は、を返し[-1, -1]ます。

  • サンプル入力

    nums = [5,7,7,8,8,10], target = 8
    
  • サンプル出力

    [3,4]
    

アイデア

この質問は典型的な二分探索の問題ですが、これは最も基本的な二分探索の問題とは少し異なります。つまり、検索された番号が繰り返される可能性がありますが、一般的な二分探索は直接検索して返すことですが、ここではできません。見つけた値が最初、最後、または中間のいずれであるかわからないため、直接戻ります。

基本的な2進数のルックアップが繰り返されないため、一般的には、while(left <= right)裁判官が直接返品を見つけた場合、返品が見つからない場合に使用-1ます。ここでは、少し改善する必要があります。周期的に決定されたものwhile(left <= right)を使用して、while(left < right)循環するために本明細書では使用されない

while(left <= right)ループアウトを使用すると、解がループから外れてはならない場合、left > right区間の[left, right]長さは負になります。使用されているwhile(left < right)ループのうち、時間、left及びright間隔、同じであってもよい[left, right]長さが1であり、そしてそれは可解場合leftright必ずしも同じで、溶液はこの時点ですleft

次に、このトピックに戻り、トピックを2つの部分に分割して、要素の最初の位置と最後の位置を見つけます。まず、検索要素の最初の位置を見つける方法、つまり検索間隔を半分にする方法を見てください。

  • target == nums[middle]:現時点では、これが必ずしも最初の要素ではないため、直接返すことはできません。範囲を狭めるにはどうすればよいですか。最初の要素、最初の要素、それともそれmiddle、またはmiddleを見つけるために、middle後ろのセクションは無視できます。さあright = middle
  • target > nums[middle]:これは基本的な二分探索と同じleft = middle + 1です。
  • target < nums[middle]:しましょうright = middle - 1

最初の要素の位置を見つけるコードスニペット全体を見てください。

public int findFisrtIndex(int[] nums, int target) {
     
     

    if (nums == null || nums.length == 0)
        return -1;

    if (nums.length == 1)
        return nums[0] == target ? 0 : -1;

    int left = 0;
    int right = nums.length - 1;
    int middle;

    while (left < right) {
     
     
        middle = left + (right - left) / 2;
        if (target == nums[middle]) {
     
     
            right = middle;
        } else if (target > nums[middle]) {
     
     
            left = middle + 1;
        } else {
     
     
            right = middle - 1;
        }
    }

    if (nums[left] == target)
        return left; 
    return -1;
}

次のステップは、最後の要素の位置を見つけることです。これは基本的に最初のアイデアと同じなので、説明はせず、後ろのコード部分を見てください。

ここでもう少し詳しく説明します。バイナリ検索を作成するときに、間隔が正しく分割されていない場合、無限ループが発生しやすくなります。つまり、要素が2つあるときに間隔を短縮できない場合、次のようになります。無限ループ。最後の要素を見つけるためのコードを書くときはmiddle、選択方法を切り上げる必要があります。最初の要素の位置を見つけるように切り下げることはできません。そうしないと、無限ループが発生します。自分で2つの要素に対して試してください。間違いを犯しやすいこと。

コード

public int[] searchRange(int[] nums, int target) {
     
     

    int firstIndex = findFisrtIndex(nums, target);
    if (firstIndex == -1)
        return new int[] {
     
     -1, -1}; 
    int lastIndex = findLastIndex(nums, target);
    return new int[] {
     
     firstIndex, lastIndex};
}

public int findFisrtIndex(int[] nums, int target) {
     
     

    if (nums == null || nums.length == 0)
        return -1;

    if (nums.length == 1)
        return nums[0] == target ? 0 : -1;

    int left = 0;
    int right = nums.length - 1;
    int middle;

    while (left < right) {
     
     
        middle = left + (right - left) / 2;
        if (target == nums[middle]) {
     
     
            right = middle;
        } else if (target > nums[middle]) {
     
     
            left = middle + 1;
        } else {
     
     
            right = middle - 1;
        }
    }

    if (nums[left] == target)
        return left; 
    return -1;
}

public int findLastIndex(int[] nums, int target) {
     
     
    if (nums == null || nums.length == 0)
        return -1;

    if (nums.length == 1)
        return nums[0] == target ? 0 : -1;

    int left = 0;
    int right = nums.length - 1;
    int middle;

    while (left < right) {
     
     
        middle = left + (right - left + 1) / 2;
        if (target == nums[middle]) {
     
     
            left = middle;
        } else if (target > nums[middle]) {
     
     
            left = middle + 1;
        } else {
     
     
            right = middle - 1;
        }
    }

    if (nums[left] == target) {
     
     
        return left;
    }
    return -1;
}

概要

最後に、この二分探索の概要を少し書きます。

  • 最も基本的な二分探索にはwhile (left <= right)、この行を使用します。問題は少し複雑で、while (left < right)より簡単に解決できます。
  • 二分探索の場合、範囲を絞り込んで、各ステップでの範囲の狭さ(タイトルnums[middle]target等号など)を明確にします
  • 区間除算の場合、要素が2つ残っていると無限ループが発生しやすいので、無限ループになるかどうかわからない場合は、2つの要素の状況を試して、修正が必要かどうかを確認してください。 。切り上げ。

おすすめ

転載: blog.csdn.net/qq_41512783/article/details/109588198