Javaにおける二分探索アルゴリズム

Javaのバイナリ検索アルゴリズム

1. 概要

この記事では、単純な線形検索に対するバイナリ検索の利点について説明し、Javaでの実装について説明します。

私たちがワイン販売ビジネスを行っており、毎日何百万ものバイヤーが私たちのアプリケーションにアクセスしているとしましょう。

私たちのアプリを介して、顧客はnドル未満の価格のアイテムを除外し、検索結果からボトルを選択して、カートに追加することができます。 毎秒価格制限のあるワインを探している何百万人ものユーザーがいます。 結果は高速である必要があります。

バックエンドでは、アルゴリズムにより、顧客が入力した価格制限とリスト内のすべてのワインボトルの価格を比較して、ワインのリスト全体を線形検索します。

次に、価格が価格制限以下のアイテムを返します。 この線形探索の時間計算量はO(n)です。

つまり、システム内のワインボトルの数が多いほど、時間がかかります。 The search time increases proportionately to the number of new items introduced.

アイテムをソートされた順序で保存し始め、バイナリ検索を使用してアイテムを検索すると、O(log n)の複雑さを実現できます。

バイナリ検索では、検索結果にかかる時間はデータセットのサイズに応じて自然に増加しますが、比例するわけではありません。

簡単に言えば、アルゴリズムはkey値を配列の中央の要素と比較します。それらが等しくない場合、キーを含めることができない半分が削除され、成功するまで残りの半分が検索されます。

ここで重要な点は、配列が既にソートされていることです。

残りの半分が空の状態で検索が終了した場合、keyは配列に含まれていません。

3.1. 反復実装

public int runBinarySearchIteratively(
  int[] sortedArray, int key, int low, int high) {
    int index = Integer.MAX_VALUE;

    while (low <= high) {
        int mid = (low + high) / 2;
        if (sortedArray[mid] < key) {
            low = mid + 1;
        } else if (sortedArray[mid] > key) {
            high = mid - 1;
        } else if (sortedArray[mid] == key) {
            index = mid;
            break;
        }
    }
    return index;
}

runBinarySearchIterativelyメソッドは、sortedArraykey、およびsortedArraylowhighインデックスを引数として取ります。 メソッドが初めてlowを実行するとき、sortedArray,の最初のインデックスは0ですが、highsortedArray,の最後のインデックスは長さ–1。

middleは、sortedArrayの中間インデックスです。 ここで、アルゴリズムはwhileループを実行し、keysortedArrayの中央のインデックスの配列値と比較します。

3.2. 再帰的実装

それでは、単純で再帰的な実装も見てみましょう。

public int runBinarySearchRecursively(
  int[] sortedArray, int key, int low, int high) {
    int middle = (low + high) / 2;

    if (high < low) {
        return -1;
    }

    if (key == sortedArray[middle]) {
        return middle;
    } else if (key < sortedArray[middle]) {
        return runBinarySearchRecursively(
          sortedArray, key, low, middle - 1);
    } else {
        return runBinarySearchRecursively(
          sortedArray, key, middle + 1, high);
    }
}

runBinarySearchRecursivelyメソッドは、sortedArraysortedArraykey,low、およびhighインデックスを受け入れます。

3.3. Arrays.binarySearch()の使用

int index = Arrays.binarySearch(sortedArray, key);

整数の配列で検索されるA sortedArrayおよびintkeyは、JavaArraysクラスのbinarySearchメソッドに引数として渡されます。 。

3.4. Collections.binarySearch()の使用

int index = Collections.binarySearch(sortedList, key);

Integerオブジェクトのリストで検索されるA sortedListIntegerkeyは、Java%のbinarySearchメソッドに引数として渡されます。 (t5)sクラス。

3.5. パフォーマンス

Whether to use a recursive or an iterative approach for writing the algorithm is mostly a matter of personal preference.しかし、ここで注意すべきいくつかのポイントがあります。

1. stackを維持するオーバーヘッドのために再帰が遅くなる可能性があり、通常はより多くのメモリを消費します2。 再帰はstack-に適していません。 ビッグデータセット3を処理するときにStackOverflowExceptionが発生する可能性があります。 再帰は、反復アプローチと比較してコードを短くするため、コードに明快さを追加します

理想的には、バイナリ検索は、nの大きな値の線形検索とは対照的に、実行する比較の数が少なくなります。 nの値が小さい場合、線形検索の方がバイナリ検索よりもパフォーマンスが向上する可能性があります。

この分析は理論的なものであり、状況によって異なる場合があることを知っておく必要があります。

また、the binary search algorithm needs a sorted data set which has its costs too。 データのソートにマージソートアルゴリズムを使用する場合、n log nの複雑さがコードに追加されます。

そのため、まず要件を十分に分析してから、どの検索アルゴリズムが要件に最も適しているかを判断する必要があります。

4. 結論

このチュートリアルでは、バイナリ検索アルゴリズムの実装と、線形検索の代わりにそれを使用することが望ましいシナリオを示しました。

チュートリアルover on GitHubのコードを見つけてください。