Menu
Coddy logo textTech

二分探索

最終更新

二分探索は、**ソート済み**の配列で探索範囲を繰り返し半分に絞ることで目的の値を見つけます。中央の要素を目的の値と比較し、一致すれば探索は終了。一致しなければ、目的の値を含み得ない半分を捨て、範囲をもう一方の半分に縮めます。1回の比較ごとに残りの要素の半分が除外されるため、実行時間は O(log n) になります — 100万個のソート済みの値を探索しても、比較は最大でも約20回で済みます。

上のアニメーションは lomidhi の各ポインタを表示し、比較のたびに除外された半分を薄く表示します。唯一の絶対条件は、配列があらかじめソート済みであること — 未ソートのデータには線形探索を使うか、先にソートが必要です(マージソートを参照)。同じ「半分に絞る」考え方は二分探索木の基盤でもあります。

時間計算量と空間計算量

ケース計算量備考
最良ケースO(1)最初の比較で中央の要素が目的の値である場合。
平均ケースO(log n)1回の比較ごとに残りの範囲が半分になる。
最悪ケースO(log n)一致または不在の確定前に、範囲が1要素まで縮む。
空間O(1)反復版は lohimid のインデックスだけを保持する。

ステップごとの説明

ステップ何が起こるか
1lo をソート済み配列の最初のインデックスに、hi を最後のインデックスに設定する。
2中央のインデックスを計算する:mid = (lo + hi) // 2
3a[mid] が目的の値と等しければ mid を返す — 発見。
4a[mid] が目的の値より**小さい**なら、目的の値は右半分にしかあり得ない:lo = mid + 1 とする。
5a[mid] が目的の値より**大きい**なら、左半分を探索する:hi = mid - 1 とする。
6lo <= hi の間、ステップ2から繰り返す。範囲が空になったら、目的の値は配列に存在しない。

具体例

[1, 2, 3, 5, 7, 8, 9] から 5 を探索する場合:

パス範囲 (lo..hi)mida[mid]動作
1[1, 2, 3, 5, 7, 8, 9] (0..6)35a[3] = 5 — インデックス3で目的の値を発見。

見つからない場合のステップ

同じ配列で 4 を探索すると、範囲が空になっていく様子がわかります:

パス範囲 (lo..hi)mida[mid]動作
10..6355 > 4 — 左半分を探索、hi = 2
20..2122 < 4 — 右半分を探索、lo = 2
32..2233 < 4lo が3になり範囲が空になる:見つからない。

二分探索を使うべき場面

使うべきとき避けるべきとき
データがすでにソート済みのとき(または何度も探索するとき)データが未ソートで1回しか探索しないとき — 先にソートすると O(n log n) かかる
コレクションが高速なランダムアクセスに対応しているとき(配列)順次アクセスしかできないとき(連結リスト)
データセットが大きいとき — O(log n) は大規模で真価を発揮するデータセットがごく小さいとき — 単純な走査でも同じくらい速く、より簡単

Binary Searchのコード

Python, JavaScript, Java, C++, C, Pseudocodeによるクリーンで実行可能なBinary Searchの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのBinary Searchのコード

Python
1def binary_search(a, target):2    lo, hi = 0, len(a) - 13    while lo <= hi:4        mid = (lo + hi) // 25        if a[mid] == target:6            return mid7        if a[mid] < target:8            lo = mid + 1  # search the right half9        else:10            hi = mid - 1  # search the left half11    return -112
13
14nums = [1, 2, 3, 5, 7, 8, 9]  # must be sorted15print("Index of 5:", binary_search(nums, 5))16print("Index of 4:", binary_search(nums, 4))
このコードをPythonプレイグラウンドで実行

二分探索のよくある質問

二分探索の時間計算量はどのくらいですか?
平均ケースと最悪ケースで O(log n) です。1回の比較ごとに残りの探索範囲が半分になるためです。最初の中央の要素が目的の値だった場合の最良ケースは O(1) です。反復版が使う追加空間は O(1) です。
なぜ二分探索にはソート済み配列が必要なのですか?
半分に絞るステップは順序に依存します。目的の値と中央の要素を比較してどちらの半分を捨てられるかがわかるのは、中央より左がすべて小さく、右がすべて大きい場合だけです。未ソートのデータではこの推論が成り立ちません — 代わりに線形探索を使うか、先にソートしてください。
二分探索と線形探索の違いは何ですか?
線形探索は要素を1つずつ走査し(O(n))、どんな配列でも動作します。二分探索はソート済み配列の探索範囲を半分ずつ絞りますが(O(log n))、入力がソート済みであることが必要です。要素が少なければ差はごくわずかですが、大規模になると二分探索が圧倒的に有利です。
二分探索に必要な比較回数はどのくらいですか?
最大でも約 log2(n) + 1 回です。10回の比較で1,000要素、20回の比較で1,000,000要素をカバーできます。この対数的な増加こそが、ソート済みデータでの標準的な探索手法とされる理由です。
二分探索の有名なオーバーフローのバグとは何ですか?
中央を (lo + hi) / 2 で計算すると、lo + hi が型の最大値を超えたときに固定サイズ整数がオーバーフローすることがあります。安全な形は mid = lo + (hi - lo) / 2 です。Python では問題になりません(任意精度整数)が、Java/C/C++ では実際に起きた有名なバグです。
二分探索と二分探索木は同じものですか?
半分に絞るという考え方は共通ですが、構造が異なります。二分探索はソート済み配列上のアルゴリズムであるのに対し、二分探索木はキーを順序どおりに保つ連結データ構造で、探索は各ノードで左または右に下っていきます。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める