二分探索でソート済み配列から値を探す

小さい順に並んだ整数から目当ての数の位置を探し、無ければ -1 を返す。探す範囲を毎回半分に狭める二分探索の練習です。

海外で定番の問題リスト 4 本のうち 2 本に入っています (NeetCode 150、Grind 75)

問題

小さい順に並んだ整数の並び nums と、整数 target を受け取ります。nums の中に target があれば、その位置 (0 から数えた番号) を返し、無ければ -1 を返す関数を書いてください。

  • nums の値は小さい順に並び、同じ値は 2 回出てきません
  • nums の長さは 0 以上 10000 以下、値はどれも -10000 以上 10000 以下です
  • 時間は O(log n) で解いてください

入力と出力の例

例 1

nums = [-6, 0, 3, 8, 15, 21] target = 8
3

例 2

並びに無い数を探すと -1 です。

nums = [2, 5, 9, 13] target = 6
-1

例 3

端の数も見つけられます。

nums = [1, 4, 10] target = 10
2

面接での聞かれ方

面接では、問題文がもっと短く渡されることが多いです。

  • 「ソート済みの配列から、目標の数のインデックスを返してください。無ければ -1 です」
  • 書き終えたあとに「なぜ O(log n) になるのですか?」と続けて聞かれます
  • 「同じ値が何個もあったら、どの位置を返しますか?」と、問題文に無い場合を聞かれることもあります

二分探索は短いコードですが、端の扱いで間違えやすい問題です。探す範囲を「両端を含む」と決めたことを先に言っておくと、そのあとの説明がぶれません。

ヒント

まだ開いていません

付箋は 3 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3答えに近い

アルゴリズムのチートシートの「ソートと二分探索」に、この型の要点を表とコードでまとめています。

二分探索は、並んだデータの範囲を半分ずつ絞ります。100 万件でも 20 回ほどで 1 件になります。

main.py
def binary_search(nums: list[int], target: int) -> int:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(binary_search([-6, 0, 3, 8, 15, 21], 8))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します