Files
hello-algo/ja/docs/chapter_searching/binary_search.md
Yudong Jin d7b2277d2b Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
2026-03-30 07:30:15 +08:00

6.0 KiB
Raw Blame History

二分探索

二分探索binary searchは分割統治法に基づく効率的な探索アルゴリズムです。データが整列済みである性質を利用し、各ラウンドで探索範囲を半分に縮小し、目標要素を見つけるか探索区間が空になるまで続けます。

!!! question

長さ $n$ の配列 `nums` が与えられます。要素は小さい順に並んでおり、重複しません。要素 `target` がこの配列内にある場合はそのインデックスを返し、含まれない場合は $-1$ を返してください。例を次の図に示します。

二分探索の例

次の図に示すように、まずポインタ i = 0j = n - 1 を初期化し、それぞれ配列の先頭要素と末尾要素を指すようにして、探索区間 [0, n - 1] を表します。角括弧は閉区間を表し、境界値自体を含むことに注意してください。

次に、以下の 2 つの手順を繰り返します。

  1. 中央のインデックス m = \lfloor {(i + j) / 2} \rfloor を計算します。ここで \lfloor \: \rfloor は切り捨てを表します。
  2. nums[m]target の大小関係を判定し、次の 3 つの場合に分かれます。
    1. nums[m] < target のとき、target は区間 [m + 1, j] にあるため、i = m + 1 を実行します。
    2. nums[m] > target のとき、target は区間 [i, m - 1] にあるため、j = m - 1 を実行します。
    3. nums[m] = target のとき、target が見つかったので、インデックス m を返します。

配列に目標要素が含まれない場合、探索区間は最終的に空まで縮小されます。このとき -1 を返します。

=== "<1>" 二分探索の流れ

=== "<2>" binary_search_step2

=== "<3>" binary_search_step3

=== "<4>" binary_search_step4

=== "<5>" binary_search_step5

=== "<6>" binary_search_step6

=== "<7>" binary_search_step7

注意すべき点として、ij はどちらも int 型であるため、i + jint 型の範囲を超える可能性があります。大きな数によるオーバーフローを避けるため、通常は式 m = \lfloor {i + (j - i) / 2} \rfloor を用いて中点を計算します。

コードは次のとおりです。

[file]{binary_search}-[class]{}-[func]{binary_search}

時間計算量は $O(\log n)$ :二分探索のループでは各ラウンドで区間が半分になるため、ループ回数は \log_2 n です。

空間計算量は $O(1)$ :ポインタ ij に必要なのは定数サイズの空間だけです。

区間の表し方

上記の両閉区間のほかに、一般的な区間表現として「左閉右開」区間があり、[0, n) と定義されます。つまり左端は含み、右端は含みません。この表現では、区間 [i, j)i = j のとき空です。

この表現に基づいて、同じ機能を持つ二分探索アルゴリズムを実装できます。

[file]{binary_search}-[class]{}-[func]{binary_search_lcro}

次の図に示すように、2 種類の区間表現では、二分探索アルゴリズムの初期化、ループ条件、区間の縮小操作がそれぞれ異なります。

「両閉区間」の表現では左右の境界がどちらも閉区間として定義されるため、ポインタ i とポインタ j による区間縮小の操作も対称になります。このほうがミスをしにくいため、一般には「両閉区間」の書き方を推奨します

2 種類の区間定義

利点と限界

二分探索は時間と空間の両面で優れた性能を持ちます。

  • 二分探索は時間効率が高いです。データ量が大きい場合、対数時間計算量は大きな優位性を持ちます。たとえば、データサイズ n = 2^{20} のとき、線形探索では 2^{20} = 1048576 回のループが必要ですが、二分探索では \log_2 2^{20} = 20 回で済みます。
  • 二分探索は追加の空間を必要としません。追加領域を要する探索アルゴリズム(たとえばハッシュ探索)と比べて、二分探索はより省メモリです。

しかし、二分探索があらゆる状況に適しているわけではなく、主な理由は次のとおりです。

  • 二分探索は整列済みデータにしか適用できません。入力データが無秩序な場合、二分探索を使うためだけにソートするのは割に合いません。ソートアルゴリズムの時間計算量は通常 O(n \log n) であり、線形探索や二分探索よりも高いからです。要素を頻繁に挿入する場面では、配列の整列性を保つために特定位置へ挿入する必要があり、その時間計算量は O(n) と高コストです。
  • 二分探索は配列にしか適していません。二分探索では要素へ飛び飛びにアクセスする必要がありますが、連結リストでそのようなアクセスを行う効率は低いため、連結リストやそれを基に実装されたデータ構造には向きません。
  • データ量が小さい場合は線形探索のほうが高性能です。線形探索では各ラウンドで 1 回の比較だけで済みますが、二分探索では 1 回の加算、1 回の除算、1 ~ 3 回の比較、1 回の加算(減算)が必要で、合計 4 ~ 6 個の基本操作になります。したがって、データ量 n が小さいときは、線形探索のほうがかえって速くなります。